add partition*

dev-refac-2020
Matthew Butterick 6 years ago
parent 0081cdaab8
commit 2fd6367303

@ -1,4 +1,4 @@
#lang racket/base #lang debug racket/base
(require (for-syntax (require (for-syntax
racket/base) racket/base)
racket/list racket/list
@ -15,15 +15,29 @@
(raise-argument-error 'trimf "list?" xs)) (raise-argument-error 'trimf "list?" xs))
(dropf-right (dropf xs test-proc) test-proc)) (dropf-right (dropf xs test-proc) test-proc))
(define (slicef-and-filter-split-helper xs pred [drop-negated? #f]) (define (slicef-and-filter-split-helper xs pred [separate-negated? #f])
(let loop ([xs xs][negating? #f][acc empty]) (let loop ([xs xs][negating? #f][acc empty][negated-acc empty])
(match xs
[(? empty?) (if separate-negated?
(values (reverse acc) (reverse negated-acc))
(reverse acc))]
[(list* (? (if negating? (negate pred) pred) pred-xs) ... other-xs)
(cond (cond
[(empty? xs) (reverse acc)] [(and negating? separate-negated?)
(loop other-xs
(not negating?)
acc
(match pred-xs
[(? empty?) negated-acc]
[_ (cons pred-xs negated-acc)]))]
[else [else
(define loop-pred (if negating? (negate pred) pred)) (loop other-xs
(define-values (loop-pred-xs other-xs) (splitf-at xs loop-pred)) (not negating?)
(define subxs (if (and negating? drop-negated?) empty loop-pred-xs)) (match pred-xs
(loop other-xs (not negating?) (if (empty? subxs) acc (cons subxs acc)))]))) [(? empty?) acc]
[_ (cons pred-xs acc)])
negated-acc)])])))
(define+provide+safe (slicef xs pred) (define+provide+safe (slicef xs pred)
(list? procedure? . -> . (listof list?)) (list? procedure? . -> . (listof list?))
@ -72,12 +86,19 @@
(match/values (split-at xs len) (match/values (split-at xs len)
[(subxs rest) (loop rest (cons subxs slices))])))) [(subxs rest) (loop rest (cons subxs slices))]))))
(define+provide+safe (partition* pred xs)
(predicate/c list? . -> . (values list? list?))
(unless (list? xs)
(raise-argument-error 'partition* "list?" xs))
(slicef-and-filter-split-helper xs pred 'drop-negated))
(define+provide+safe (filter-split xs pred) (define+provide+safe (filter-split xs pred)
(list? predicate/c . -> . (listof list?)) (list? predicate/c . -> . (listof list?))
(unless (list? xs) (unless (list? xs)
(raise-argument-error 'filter-split "list?" xs)) (raise-argument-error 'filter-split "list?" xs))
;; same idea as slicef, but the negated items are dropped(- ;; same idea as slicef, but the negated items are dropped
(slicef-and-filter-split-helper xs (negate pred) 'drop-negated)) (define-values (negated-pred-xs _) (partition* (negate pred) xs))
negated-pred-xs)
(define+provide+safe (frequency-hash xs) (define+provide+safe (frequency-hash xs)
(list? . -> . hash?) (list? . -> . hash?)

@ -1,9 +1,9 @@
#lang scribble/manual #lang scribble/manual
@(require scribble/eval (for-label racket sugar)) @(require scribble/eval (for-label racket sugar racket/function))
@(define my-eval (make-base-eval)) @(define my-eval (make-base-eval))
@(my-eval `(require sugar racket/list)) @(my-eval `(require sugar racket/list racket/function))
@title{Lists} @title{Lists}
@defmodule[#:multi (sugar/list (submod sugar/list safe))] @defmodule[#:multi (sugar/list (submod sugar/list safe))]
@ -33,9 +33,20 @@ Like @racket[string-split], but for lists. Drop elements from anywhere in @racke
@examples[#:eval my-eval @examples[#:eval my-eval
(filter-split '(1 a b c 2 d e f 3) integer?) (filter-split '(1 a b c 2 d e f 3) integer?)
(filter-split '(1 a b c 2 d e f 3) (compose not integer?)) (filter-split '(1 a b c 2 d e f 3) (negate integer?))]
(filter-split '(a b c 1 2 3 d e f) integer?)
(filter-split '(a b c 1 2 3 d e f) (compose not integer?))] @defproc[
(partition*
[pred procedure?]
[lst list?])
(values list? list?)]
Like @racket[partition], but contiguous groups of elements matching (or not matching) @racket[_pred] are kept together in sublists.
Same as @racket[(values (filter-split _lst _pred) (filter-split _lst (negate _pred)))], but only traverses the list once.
@examples[#:eval my-eval
(partition* integer? '(1 a b c 2 d e f 3))
(partition* (negate integer?) '(1 a b c 2 d e f 3))]
@defproc[ @defproc[
(slice-at (slice-at

@ -132,7 +132,9 @@
(check-equal? (trimf (list 4 1 2 3 4) even?) '(1 2 3)) (check-equal? (trimf (list 4 1 2 3 4) even?) '(1 2 3))
(check-equal? (trimf (list 1 3 2 4 5 6 8 9 13) odd?) '(2 4 5 6 8)) (check-equal? (trimf (list 1 3 2 4 5 6 8 9 13) odd?) '(2 4 5 6 8))
(check-equal? (filter-split '(1 2 3 4 5 6) even?) '((1)(3)(5))) (check-equal? (filter-split '(1 1 2 3 4 4 5 6) even?) '((1 1)(3)(5)))
(check-equal? (let-values ([(preds not-preds) (partition* even? '(1 1 2 3 4 4 5 6))])
(list preds not-preds)) (list '((2)(4 4)(6)) '((1 1)(3)(5))))
(define foo-path-strings '("foo" "foo.txt" "foo.bar" "foo.bar.txt")) (define foo-path-strings '("foo" "foo.txt" "foo.bar" "foo.bar.txt"))
(match-define (list foo-path foo.txt-path foo.bar-path foo.bar.txt-path) (map ->path foo-path-strings)) (match-define (list foo-path foo.txt-path foo.bar-path foo.bar.txt-path) (map ->path foo-path-strings))

Loading…
Cancel
Save