diff --git a/list.rkt b/list.rkt index 3966ff5..56ce6a6 100644 --- a/list.rkt +++ b/list.rkt @@ -1,5 +1,5 @@ #lang racket/base -(require racket/list) +(require racket/list racket/set) (require "define.rkt" "len.rkt" "coerce.rkt") (define+provide/contract (trimf xs test-proc) @@ -28,7 +28,7 @@ (loop rest (cons item acc)))))) -(define+provide/contract (count-incidence x) +(define+provide/contract (frequency-hash x) (list? . -> . hash?) (define counter (make-hash)) (for ([item (flatten x)]) @@ -36,22 +36,26 @@ counter) + + + (define+provide/contract (members-unique? x) - (any/c . -> . boolean?) + ((or/c list? vector? string?) . -> . boolean?) (cond - [(list? x) (= (len (remove-duplicates x)) (len x))] - [(vector? x) (members-unique? (vector->list x))] - [(string? x) (members-unique? (string->list x))] - [else (error (format "members-unique cannot be determined for ~a" x))])) + [(list? x) (= (len (remove-duplicates x)) (len x))] + [(vector? x) (->list x)] + [(string? x) (string->list x)] + [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)) (if (not result) - (let* ([duplicate-keys (filter-not empty? (hash-map (count-incidence x) + (let* ([duplicate-keys (filter-not empty? (hash-map (frequency-hash x) (λ(k v) (if (> v 1) k '()))))]) - (error (string-append (if (= (len duplicate-keys) 1) + (error (string-append "members-unique? failed because " (if (= (len duplicate-keys) 1) "item isn’t" "items aren’t") " unique:") duplicate-keys)) result)) \ No newline at end of file diff --git a/scribblings/list.scrbl b/scribblings/list.scrbl new file mode 100644 index 0000000..d7517ef --- /dev/null +++ b/scribblings/list.scrbl @@ -0,0 +1,76 @@ +#lang scribble/manual + +@(require scribble/eval (for-label racket sugar)) + +@(define my-eval (make-base-eval)) +@(my-eval `(require sugar)) + +@title{List} +@defmodule[sugar/list] + + +@defproc[ +(trimf +[lst list?] +[pred procedure?]) +list?] +Drop elements from each end of @racket[_lst] that satisfy @racket[_pred]. Exactly equivalent to @racket[(dropf-right (dropf _lst _pred) _pred)]. + +@examples[#:eval my-eval +(trimf '(1 2 3 a b c 4 5 6) integer?) +(trimf '(1 2 3 a b c) integer?) +(trimf '(a b c) integer?) +(trimf '(a b c 1 2 3 d e f) integer?)] + + +@defproc[ +(filter-split +[lst list?] +[pred procedure?]) +(listof list?)] +Like @racket[string-split], but for lists. Drop elements from anywhere in @racket[_lst] that satisfy @racket[_pred] — ends, middle, you name it — and return a list of the sublists that remain. + +@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) (compose not 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[ +(frequency-hash +[lst list?]) +hash?] +Count the frequency of each element in @racket[_lst], and return a hash whose keys are the unique elements of @racket[_lst], and each value is the frequency of that element within @racket[_lst]. + +@examples[#:eval my-eval +(frequency-hash '(a b b c c c)) +(frequency-hash '(c b c a b c)) +] + + + +@defproc[ +(members-unique? +[container (or/c list? vector? string?)]) +boolean?] +Return @racket[#t] if every element in @racket[_container] is unique, otherwise @racket[#f]. + +@examples[#:eval my-eval +(members-unique? '(a b c d e f)) +(members-unique? '(a b c d e f a)) +] + +@defproc[ +(members-unique?/error +[container (or/c list? vector? string?)]) +boolean?] +Same as @racket[members-unique?], but if the members are not unique, raises a descriptive error rather than returning @racket[#f]. + +@examples[#:eval my-eval +(members-unique?/error '(a b c d e f)) +(members-unique?/error '(a b c d e f a)) +(members-unique?/error '(a b c d e f a b)) +] + + diff --git a/scribblings/sugar.scrbl b/scribblings/sugar.scrbl index a5991b2..dcb252a 100644 --- a/scribblings/sugar.scrbl +++ b/scribblings/sugar.scrbl @@ -28,6 +28,8 @@ A collection of small functions to help make Racket code simpler & more readable @include-section["len.scrbl"] +@include-section["list.scrbl"] + @include-section["license.scrbl"] @index-section[] diff --git a/tests.rkt b/tests.rkt index 840cede..7134d94 100644 --- a/tests.rkt +++ b/tests.rkt @@ -93,9 +93,9 @@ (check-false ("foobar" . ends-with? . "foo")) ; (check-equal? (trim (list "\n" " " 1 2 3 "\n") whitespace?) '(1 2 3)) -(check-equal? (trim (list 1 3 2 4 5 6 8 9 13) odd?) '(2 4 5 6 8)) -;(check-equal? (splitf-at* '("foo" " " "bar" "\n" "\n" "ino") whitespace?) '(("foo")("bar")("ino"))) -(check-equal? (splitf-at* '(1 2 3 4 5 6) even?) '((1)(3)(5))) +(check-equal? (trimf (list 1 3 2 4 5 6 8 9 13) odd?) '(2 4 5 6 8)) +;(check-equal? (filter-split '("foo" " " "bar" "\n" "\n" "ino") whitespace?) '(("foo")("bar")("ino"))) +(check-equal? (filter-split '(1 2 3 4 5 6) even?) '((1)(3)(5))) (check-equal? (filter-tree string? '(p)) null)