git.fiddlerwoaroof.com
lens.lisp
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))