(def include? (thing things) ; alias for 'detect ; true if thing is in things, nil otherwise (detect thing things)) (def sort-by (f things) ; sort 'things according to the value ; returned by 'f for each thing in 'things (let tmp (hash) (each thing things (hash-cons tmp (f thing) thing)) (apply joinlists (map λx(hash-get tmp x) (sort:hash-keys tmp))))) (def mapreduce (fmap freduce things) ; same as (reduce freduce (map fmap things)) ; returns the resulting list (if (pair? things) (freduce (fmap (car things)) (mapreduce fmap freduce (cdr things))) things (freduce (map fmap things)) (freduce))) ; map 'f over 'things and sum the resulting list (def mapsum (f things) (mapreduce f + things)) ; return values for each key in hash 'h (def hash-values (h) (map (fn (k) h.,k) (hash-keys h))) (def seen? () ; returns a new function f which takes a parameter x ; for each call to f with any value Z for x ; f returns true if this f has previously seen Z ; f returns nil otherwise. ; Note that each call to 'seen? returns a new function with ; its own history independent of previous calls to 'seen? (let seen (hash) λx(returning seen.,x (= seen.,x t)))) ; return a list containing all the elements of 'things, but with no duplicates (def uniqify (things) (reject (seen?) things)) (def group-by (f things) ; return a hash of 'things keyed by (f thing) for ; each thing in 'things (returnlet hsh {} (each thing things (hash-cons hsh (f thing) thing)))) (with (m2i λd(+ (* 12 d.year) (- d.month 1)) i2m λi(date (/ i 12) (+ 1 (mod i 12)) 1)) (def relative-months (anchor . mm) ; 'anchor is a date ; 'mm is a list of integers ; for each m in 'mm, return the date at the beginning of ; the month given by adding m months to 'anchor (let mi (m2i anchor) (map λm(i2m (+ mi m)) mm)))) ; (auto-hash a b c) same as { a a b b c c } (mac auto-hash names `(brace-list ,@(flatten:map λn(list n n) names))) (mac accum (accfn-name . body) (w/uniq acc `(let ,acc nil (let ,accfn-name λa(push a ,acc) ,@body (rev ,acc))))) ; increment the value at 'place by 'inc (default 1) (mac ++ (place inc) `(= ,place (+ ,place ,(or inc 1)))) ; return a function that returns 'start on first invocation, ; and 'start + n * 'incr for each nth invocation (def seqf (start incr) (let i (or incr 1) (fn () (returning start (++ start i))))) ; like 'map, but assumes each item in 'args is a list ; of parameters for 'f. Effectively, calls (apply f item) ; for each item in 'args (def mapply (f args) (map λa(apply f a) args)) (mac def/cycler (name things) ; create a function called 'name ; each invocation of the function will ; return the next value in 'things, cycling around to the start if no things are left `(with (i -1 xs ',things list-len ,(len things)) (def ,name (j) (comment ,(just "each call to ~name returns the next value from ~(inspect things)")) (nth (= i (mod (+ 1 (or j i)) list-len)) xs)))) (def bucket/fill (items bucket size-f bucket-size maximum-size) ; returns a list (list a b c) where ; 'a is a subset of 'items ; 'b is the sum of sizes of items in 'a : (apply + (map size-f a)) ; 'c is the subset of 'items not in 'a ; invariants: ; b < maximum-size ; 'a + 'c is equal to 'items ; arguments: ; 'items is the list of things of which you have too many ; 'bucket is either nil, or a list if you have an existing partially-filled bucket ; 'size-f is a function that can tell the size of each item in 'items ; 'bucket-size is the size of the existing bucket, or 0 if empty ; 'maximum-size is the maximum allowed size for the bucket ; implementation note: this function exploits the behaviour of '> returning its last argument when true (aif (and items (> maximum-size (+ (size-f (car items)) bucket-size))) (bucket/fill (cdr items) (cons (car items) bucket) size-f it maximum-size) (and items (eq? bucket-size 0)) (bucket/fill (cdr items) (cons (car items) bucket) size-f (size-f (car items)) maximum-size) (list (rev bucket) bucket-size items))) (def bucket/new (buckets) ; used by 'fill-buckets (cons { bucket-size 0 } buckets)) (def fill-buckets (items max buckets size-f key) ; useful for pagination where each item may have a different size ; returns a list of hash with keys 'bucket-size and key ; if buckets is non-nil, assumes it is a list of previously-established buckets ; will add new items to first bucket if its 'bucket-size permits (if items (if buckets (let initial (car buckets) (let (these size others) (bucket/fill items nil size-f initial.bucket-size max) (hash-set initial key these) (= initial.bucket-size size) (if others (fill-buckets others max (bucket/new buckets) size-f key) (fill-buckets others max buckets size-f key)))) (fill-buckets items max (bucket/new buckets) size-f key)) buckets))