diff --git a/list.rkt b/list.rkt index d1186b4..6ed4618 100644 --- a/list.rkt +++ b/list.rkt @@ -9,6 +9,7 @@ (define (list-of-lists? xs) (and (list? xs) (andmap list? xs))) + (define+provide/contract (slicef-at xs pred [force? #f]) ((list? procedure?) (boolean?) . ->* . list-of-lists?) (define-values (last-list list-of-lists) @@ -25,6 +26,19 @@ (cdr list-of-lists) list-of-lists))) + +(define+provide/contract (slicef-after xs pred) + (list? procedure? . -> . list-of-lists?) + (define-values (last-list list-of-lists) + (for/fold ([current-list empty][list-of-lists empty])([x (in-list xs)]) + (if (pred x) + (values empty (cons (reverse (cons x current-list)) list-of-lists)) + (values (cons x current-list) list-of-lists)))) + (reverse (if (empty? last-list) + list-of-lists + (cons (reverse last-list) list-of-lists)))) + + (define+provide/contract (slice-at xs len [force? #f]) ((list? (and/c integer? positive?)) (boolean?) . ->* . list-of-lists?) (define-values (last-list list-of-lists) @@ -36,10 +50,7 @@ list-of-lists (cons (reverse last-list) list-of-lists)))) -;; this function could possibly be implemented as a post-processing step on slicef-at: -;; every list after the first list starts with an element matching pred. -;; so drop the first element from all lists except the first. -;; (cons (car xs) (map cdr (cdr xs))) + (define+provide/contract (filter-split xs pred) (list? predicate/c . -> . list-of-lists?) (define-values (last-list list-of-lists) @@ -72,7 +83,6 @@ [else (error (format "members-unique? cannot be determined for ~a" x))])) - (define+provide/contract (members-unique?/error x) (any/c . -> . boolean?) (define result (members-unique? x)) diff --git a/scribblings/list.scrbl b/scribblings/list.scrbl index dafc818..5f573bb 100644 --- a/scribblings/list.scrbl +++ b/scribblings/list.scrbl @@ -65,9 +65,22 @@ Divide @racket[_lst] into sublists starting with elements matching @racket[_pred @examples[#:eval my-eval (slicef-at (range 5) even?) (slicef-at '(1 2 2 1 2) even?) +(slicef-at '(1 2 2 1 2) even? #t) (slicef-at (range 5) odd?) (slicef-at (range 5) odd? #t)] +@defproc[ +(slicef-after +[lst list?] +[pred procedure?]) +(listof list?)] +Divide @racket[_lst] into sublists ending with elements matching @racket[_pred]. (Distinct from @racket[slicef-at], which gives you sublists that @italic{start} with elements matching @racket[_pred].) If none of the elements match @racket[_pred], there is no slice to be made, and the result is the whole input list. + +@examples[#:eval my-eval +(slicef-after '(1 2 2 1 2) even?) +(slicef-after (range 5) odd?) +(slicef-after (range 5) string?)] + @defproc[ (frequency-hash @@ -132,6 +145,8 @@ Convert @racket[_values] to a simple list. list?] Return a sublist of the @racket[_lst] starting with item @racket[_start-idx] and ending one item @bold{before} item @racket[_end-idx]. (Similar to how list slices are denominated in Python.) Thus the maximum value for @racket[_end-idx] is @racketfont{(length @racket[_lst])}. Errors will be triggered by nonsensical values for @racket[_end-idx]. +Bear in mind that @racket[sublist] is built for convenience, not performance. If you need to do a lot of random access into the middle of an ordered sequence of items, you'd be better off putting them into a @racket[vector] and using @racket[vector-copy]. + @examples[#:eval my-eval (sublist '(0 1 2 3 4 5 6 7 8) 0 8) (sublist '(0 1 2 3 4 5 6 7 8) 8 9)