4c31b0b9 |
(in-package :data-lens)
|
800a03e0 |
(declaim
|
72c5d331 |
(inline data-lens:over data-lens:transform-tail
data-lens:applicable-when data-lens:of-min-length
data-lens:on data-lens:over data-lens:slice
data-lens:compress-runs data-lens:combine-matching-lists
data-lens:juxt data-lens:element data-lens:sorted))
|
f7f7b3e6 |
|
07b0c028 |
(defgeneric functionalize (it)
(:method ((it hash-table))
(lambda (key &optional default)
(gethash key it default)))
(:method ((it vector))
(lambda (idx &optional default)
(let ((present-p (and (>= idx 0)
(< idx (length it)))))
(values (if present-p
(aref it idx)
default)
present-p))))
(:method ((it symbol))
(fdefinition it))
(:method ((it function))
it))
|
f51e3059 |
(define-compiler-macro functionalize (&whole whole it)
(typecase it
(cons (destructuring-bind (h . tail) it
(declare (ignore tail))
(case h
(quote it)
(function it)
(t whole))))
(t whole)))
|
0850311d |
;;; TODO: consider making this wrap defalias?
|
4c31b0b9 |
(defmacro shortcut (name function &body bound-args)
`(eval-when (:load-toplevel :compile-toplevel :execute)
(setf (fdefinition ',name)
(,function ,@bound-args))))
(defmacro defun-ct (name (&rest args) &body body)
`(eval-when (:load-toplevel :compile-toplevel :execute)
(defun ,name ,args
,@body)))
|
289289e7 |
(defmacro let-fn ((&rest bindings) &body body)
(let ((binding-forms (mapcar (lambda (form)
`(,(car form) ,(cadr form)
|
f909394c |
(funcall ,@(cddr form) ,@(cadr form))))
|
289289e7 |
bindings)))
`(labels ,binding-forms
,@body)))
(defgeneric extract-key (map key)
(:method ((map hash-table) key)
(gethash key map))
(:method ((map list) key)
(typecase (car map)
(cons (cdr (assoc key map :test 'equal)))
(t (loop for (a-key . value) on map by #'cddr
|
f909394c |
when (equal key a-key) do
(return (car value)))))))
|
289289e7 |
|
6447bcc0 |
(defun == (target &key (test 'eql))
|
1dcfeff5 |
(lambda (v)
(funcall test target v)))
|
6447bcc0 |
(defun deduplicate (&optional (test 'eql))
|
289289e7 |
(lambda (it)
(remove-duplicates it :test test)))
(defun cons-new (&key (test 'eql) (key 'identity))
(lambda (acc next)
(if (and acc
(funcall test
(funcall key (car acc))
(funcall key next)))
acc
(cons next acc))))
|
48ff3c11 |
(defun matching-list-reducer (test acc next)
(if (and acc
(funcall test (caar acc) (car next)))
(cons (cons (caar acc)
(append (cdar acc)
(cdr next)))
(cdr acc))
(cons next acc)))
|
289289e7 |
(defun combine-matching-lists (&key (test 'eql) &allow-other-keys)
(lambda (acc next)
|
48ff3c11 |
(matching-list-reducer test acc next)))
|
289289e7 |
|
6447bcc0 |
(defun compress-runs (&key (collector 'cons-new) (test 'eql) (key 'identity))
|
289289e7 |
(lambda (it)
(nreverse
(reduce (funcall collector :test test :key key)
it
:initial-value ()))))
|
6447bcc0 |
(defun of-length (len)
|
f7f7b3e6 |
(lambda (it)
(= (length it)
len)))
|
6447bcc0 |
(defun of-min-length (len)
|
f7f7b3e6 |
(lambda (it)
(>= (length it)
len)))
|
6447bcc0 |
(defun of-max-length (len)
|
f7f7b3e6 |
(lambda (it)
|
0a90adbe |
(<= (length it)
|
f7f7b3e6 |
len)))
|
902adc1f |
(defun applicable-when (fun test &optional (default nil default-p))
(if default-p
(lambda (it)
(if (funcall test it)
(funcall fun it)
default))
(lambda (it)
(if (funcall test it)
(funcall fun it)
it))))
|
f7f7b3e6 |
|
84eae95b |
(defmacro conj (&rest fns)
(let ((dat (gensym "dat")))
`(lambda (,dat)
|
0a90adbe |
(declare (ignorable ,dat))
|
84eae95b |
(and ,@(mapcar (lambda (fn)
`(funcall ,fn ,dat))
fns)))))
(defmacro disj (&rest fns)
(let ((dat (gensym "dat")))
`(lambda (,dat)
|
49358627 |
(declare (ignorable ,dat))
|
84eae95b |
(or ,@(mapcar (lambda (fn)
`(funcall ,fn ,dat))
fns)))))
|
6447bcc0 |
(defun sorted (comparator &rest r &key key)
|
289289e7 |
(declare (ignore key))
(lambda (it)
|
48ff3c11 |
(apply #'stable-sort (copy-seq it) comparator r)))
|
289289e7 |
|
6447bcc0 |
(defun element (num)
|
289289e7 |
(lambda (it)
(elt it num)))
|
6447bcc0 |
(defun key (key)
|
289289e7 |
(lambda (map)
(declare (dynamic-extent map))
(extract-key map key)))
|
f75af164 |
(defun keys (key &rest keys)
(lambda (map)
(loop for key in (cons key keys)
for cur = (extract-key map key) then (extract-key cur key)
finally (return cur))))
|
6447bcc0 |
(defun regex-match (regex)
|
4c31b0b9 |
(lambda (data)
(cl-ppcre:scan-to-strings regex data)))
|
6447bcc0 |
(defun include (pred)
|
4c31b0b9 |
(lambda (seq)
(remove-if-not pred seq)))
|
6447bcc0 |
(defun exclude (pred)
|
4c31b0b9 |
(lambda (seq)
(remove-if pred seq)))
|
6447bcc0 |
(defun pick (selector)
|
4c31b0b9 |
(lambda (seq)
(map 'list selector seq)))
|
22076311 |
(defun tap (cb)
(lambda (it)
(prog1 it
(funcall cb it))))
|
7187d8e4 |
(defun slice (start &optional end)
(lambda (it)
(subseq it start end)))
|
6447bcc0 |
(defun update (thing fun &rest args)
|
14b91cf5 |
(apply fun thing args))
(define-modify-macro updatef (fun &rest args)
update)
|
6447bcc0 |
(defun suffixp (suffix &key (test 'eql test-p))
|
cf67a11e |
(lambda (it)
(if test-p
(alexandria:ends-with-subseq suffix
it
:test test)
(alexandria:ends-with-subseq suffix
it))))
|
ea7e5638 |
(defun of-type (type)
(lambda (it)
(when (typep it type)
it)))
|
6447bcc0 |
(defun transform-head (fun)
|
f7f7b3e6 |
(lambda (it)
|
14b91cf5 |
(typecase it
(list (list* (funcall fun (car it))
(cdr it)))
(vector (let ((result (copy-seq it)))
(prog1 result
(updatef (elt result 0) fun)))))))
|
f7f7b3e6 |
|
6447bcc0 |
(defun transform-tail (fun)
|
7187d8e4 |
(lambda (it)
|
14b91cf5 |
(typecase it
(list (list* (car it)
(funcall fun (cdr it))))
(vector (let ((result (copy-seq it)))
(prog1 result
(updatef (subseq result 1)
fun)))))))
|
7187d8e4 |
|
6447bcc0 |
(defun splice-elt (elt fun)
|
bd1882ec |
(lambda (it)
(append (subseq it 0 elt)
(funcall fun (nth elt it))
(subseq it (1+ elt)))))
|
6447bcc0 |
(defun transform-elt (elt fun)
|
a1ca234d |
(lambda (it)
|
b7f020d3 |
(concatenate (type-of it)
(subseq it 0 elt)
(list (funcall fun (elt it elt)))
(subseq it (1+ elt)))))
|
a1ca234d |
|
6447bcc0 |
(defun key-transform (fun key-get key-set)
|
4c31b0b9 |
(lambda (it)
(let ((key-val (funcall key-get it)))
(funcall key-set
(funcall fun key-val)))))
|
6447bcc0 |
(defun juxt (fun1 &rest r)
|
f7f7b3e6 |
(lambda (&rest args)
(list* (apply fun1 args)
(when r
(mapcar (lambda (f)
(apply f args))
r)))))
|
4c31b0b9 |
|
0a90adbe |
(defun delay ()
"Return a function that always returns the last thing it was called with"
(let ((result nil))
(lambda (v)
(prog1 result
(setf result v)))))
|
18dbfba4 |
(defun =>> (fun1 fun2)
(lambda (i)
(prog1 (funcall fun1 i)
(funcall fun2))))
|
6447bcc0 |
(defun derive (diff-fun &key (key #'identity))
|
14b91cf5 |
(lambda (seq)
(typecase seq
(list (cons (cons nil (car seq))
(mapcar (lambda (next cur)
(cons (funcall diff-fun
(funcall key next)
(funcall key cur))
next))
(cdr seq)
seq)))
(vector (coerce (loop for cur = nil then next
for next across seq
if cur
collect (cons (funcall diff-fun
(funcall key next)
(funcall key cur))
cur)
else collect (cons nil next))
'vector)))))
|
f7f7b3e6 |
|
6447bcc0 |
(defun inc (inc)
|
47d7f624 |
(declare (optimize (speed 3)))
(lambda (base)
(+ base inc)))
|
6447bcc0 |
(defun cumsum
|
f7f7b3e6 |
(&key (add-fun #'+) (key #'identity) (combine (lambda (x y) y x)) (zero 0))
|
4c31b0b9 |
(lambda (seq)
(nreverse
(reduce (lambda (accum next)
(let ((key-val (funcall key next))
(old-val (if accum
(funcall key (car accum))
zero)))
(cons (funcall combine
(funcall add-fun old-val key-val)
next)
accum)))
seq
:initial-value ()))))
|
9c4d2954 |
(defun over (fun &rest funs)
(let* ((fun (functionalize fun))
(rt-pos (position :result-type funs))
(result-type (cond
((null rt-pos) 'list)
((>= (1+ rt-pos)
(length funs))
(error "invalid result-type"))
(t
(elt funs (1+ rt-pos)))))
(funs (if rt-pos
(append (mapcar #'functionalize
(subseq funs 0 rt-pos))
(mapcar #'functionalize
(subseq funs (+ rt-pos 2))))
(mapcar #'functionalize funs)))
(combined-fun (if funs
(apply #'alexandria:compose fun funs)
fun)))
(lambda (seq &rest seqs)
(if seqs
(apply #'map result-type combined-fun seq seqs)
(map result-type combined-fun seq)))))
|
4c31b0b9 |
|
6447bcc0 |
(defun denest (&key (result-type 'list))
|
14b91cf5 |
(lambda (seq)
(apply #'concatenate result-type
seq)))
|
2705341b |
(defun transform (arg &rest args)
(if args
(lambda (fn)
(apply fn arg args))
(lambda (fn)
(funcall fn arg))))
|
c9dbe210 |
(defmacro calling (fun &rest args)
(alexandria:with-gensyms (first-arg)
`(lambda (,first-arg)
(funcall (functionalize ,fun) ,first-arg ,@args))))
|
e833e39e |
(defmacro calling* (fun &rest args)
(alexandria:with-gensyms (last-arg)
`(lambda (,last-arg)
(funcall (functionalize ,fun) ,@args ,last-arg))))
|
bd1882ec |
(defmacro applying (fun &rest args)
|
d2324970 |
(alexandria:with-gensyms (seq fsym)
`(let ((,fsym (functionalize ,fun)))
(lambda (,seq)
(apply ,fsym ,@args ,seq)))))
|
bd1882ec |
|
6447bcc0 |
(defun on (fun key)
|
858bd9d2 |
"Transform arguments with KEY and then apply FUN
> (eql (funcall (on 'equal 'car)
> '(\"a\" 1 2)
> '(\"a\" 2 e))
> t)"
|
d2324970 |
(let ((fun (functionalize fun))
(key (functionalize key)))
|
858bd9d2 |
(lambda (&rest its)
|
6b839632 |
(apply fun (mapcar key its)))))
|
f7f7b3e6 |
(defun filler (length1 length2 fill-value)
(if (< length1 length2)
(make-sequence 'vector (- length2 length1) :initial-element fill-value)
#()))
|
6447bcc0 |
(defun zipping (result-type &key (fill-value nil fill-value-p))
|
f7f7b3e6 |
(lambda (seq1 seq2)
(let ((length1 (when fill-value-p (length seq1)))
(length2 (when fill-value-p (length seq2))))
(let ((seq1 (if fill-value-p
(concatenate result-type
seq1
(filler length1 length2 fill-value))
seq1))
(seq2 (if fill-value-p
(concatenate result-type
seq2
(filler length2 length1 fill-value))
seq2)))
(map result-type #'list
seq1 seq2)))))
|
6447bcc0 |
(defun maximizing (relation measure)
|
f7f7b3e6 |
(lambda (it)
(let ((it-length (length it)))
(when (> it-length 0)
(values-list
|
72a785dc |
(reduce (lambda (|arg1764| |arg1765|)
(destructuring-bind (cur-max max-idx) |arg1764|
(destructuring-bind (next next-idx) |arg1765|
(if (funcall relation (funcall measure cur-max) (funcall measure next))
(list next next-idx)
(list cur-max max-idx)))))
|
f7f7b3e6 |
(funcall (zipping 'vector)
it
(alexandria:iota it-length))))))))
|
04f22d37 |
|
6447bcc0 |
(defun group-by (fn &key (test 'equal))
|
702332c0 |
(lambda (seq)
(let ((groups (make-hash-table :test test)))
(map nil
(lambda (it)
(push it
(gethash (funcall fn it)
groups)))
seq)
(mapcar (lambda (it)
(cons (car it)
(reverse (cdr it))))
(alexandria:hash-table-alist groups)))))
|
11c6fbe1 |
(defun x-group (fn)
(lambda (groups)
(loop for (key . group) in groups
collect (funcall fn key group))))
|
989454fc |
(defun hash-join (probe join-fn &key (test 'eql) (key 'car))
(let* ((lookup (make-hash-table :test test :size (length probe)))
(lookup-fn (functionalize lookup)))
(map nil
(lambda (it)
(setf (gethash (funcall key it)
lookup)
it))
probe)
(lambda (collection)
(map (etypecase collection
(list 'list)
(vector 'vector)
(sequence 'list))
(lambda (it)
(let* ((key-value (funcall key it))
(matching-probe (funcall lookup-fn key-value)))
(funcall join-fn it matching-probe)))
collection))))
|
1f0a4f7b |
#+nil
|
04f22d37 |
(defmacro <> (arity &rest funs)
(let ((arg-syms (loop repeat arity collect (gensym))))
`(lambda (,@arg-syms)
(declare (dynamic-extent ,@arg-syms))
,(fw.lu:rollup-list (mapcar (lambda (x)
(etypecase x
(list `(funcall ,x))
(symbol (list x))))
funs)
arg-syms))))
(defmacro <>1 (&rest funs)
|
1f0a4f7b |
`(alexandria:compose ,@funs))
|
800a03e0 |
(defmacro • (&rest funs)
|
4f6b7ef2 |
`(alexandria:compose ,@funs))
(defmacro ∘ (&rest funs)
|
800a03e0 |
`(alexandria:compose ,@funs))
|