git.fiddlerwoaroof.com
Browse code

Add some useful utilities and an example

Ed Langley authored on 18/08/2018 19:55:10
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,23 @@
1
+* Intro
2
+
3
+This library provides a language for expressing data manipulations as
4
+the composition of more primitive operations.
5
+
6
+#+BEGIN_SRC lisp
7
+  DATA-LENS> (funcall (on (compress-runs :collector 'combine-matching-lists)
8
+                          (alexandria:compose 
9
+                           (over (juxt (element 0)
10
+                                       'identity))
11
+                           (sorted 'string<)))
12
+                      '("January"   "February" "March"    "April"
13
+                        "May"       "June"     "July"     "August"
14
+                        "September" "October"  "November" "December"))
15
+  #| ==> ((#\A "April" "August") 
16
+          (#\D "December")
17
+          (#\F "February")
18
+          (#\J "January" "July" "June")
19
+          (#\M "March" "May")
20
+          (#\N "November")
21
+          (#\O "October")
22
+          (#\S "September")) |#
23
+#+END_SRC
... ...
@@ -3,7 +3,12 @@
3 3
   (:export #:regex-match #:include #:exclude #:pick
4 4
            #:snapshot-to-vector #:vector-to-lt #:key-transform
5 5
            #:combine #:derive #:cumsum #:over #:on #:shortcut
6
-           #:defun-ct))
6
+           #:defun-ct
7
+           #:key
8
+           #:extract-key
9
+           #:element
10
+           #:let-fn
11
+           #:juxt))
7 12
 (in-package :data-lens)
8 13
 
9 14
 (defmacro shortcut (name function &body bound-args)
... ...
@@ -16,6 +21,68 @@
16 21
      (defun ,name ,args
17 22
        ,@body)))
18 23
 
24
+(defmacro let-fn ((&rest bindings) &body body)
25
+  (let ((binding-forms (mapcar (lambda (form)
26
+                                 `(,(car form) ,(cadr form)
27
+                                    (funcall ,@(cddr form) ,@(cadr form))))
28
+                               bindings)))
29
+    `(labels ,binding-forms
30
+       ,@body)))
31
+
32
+(defgeneric extract-key (map key)
33
+  (:method ((map hash-table) key)
34
+    (gethash key map))
35
+  (:method ((map list) key)
36
+    (typecase (car map)
37
+      (cons (cdr (assoc key map :test 'equal)))
38
+      (t (loop for (a-key . value) on map by #'cddr
39
+            when (equal key a-key) do
40
+              (return (car value)))))))
41
+
42
+(defun-ct deduplicate (&optional (test 'eql))
43
+  (lambda (it)
44
+    (remove-duplicates it :test test)))
45
+
46
+(defun cons-new (&key (test 'eql) (key 'identity))
47
+  (lambda (acc next)
48
+    (if (and acc
49
+             (funcall test
50
+                      (funcall key (car acc))
51
+                      (funcall key next)))
52
+        acc
53
+        (cons next acc))))
54
+
55
+(defun combine-matching-lists (&key (test 'eql) &allow-other-keys)
56
+  (lambda (acc next)
57
+    (if (and acc
58
+             (funcall test (caar acc) (car next)))
59
+        (cons (cons (caar acc)
60
+                    (append (cdar acc)
61
+                            (cdr next)))
62
+              (cdr acc))
63
+        (cons next acc))))
64
+
65
+(defun-ct compress-runs (&key (collector 'cons-new) (test 'eql) (key 'identity))
66
+  (lambda (it)
67
+    (nreverse
68
+     (reduce (funcall collector :test test :key key)
69
+             it
70
+             :initial-value ()))))
71
+
72
+(defun-ct sorted (comparator &rest r &key key)
73
+  (declare (ignore key))
74
+  (lambda (it)
75
+    (apply #'sort (copy-seq it) comparator r)))
76
+
77
+(defun-ct element (num)
78
+  (lambda (it)
79
+    (elt it num)))
80
+
81
+(defun-ct key (key)
82
+  (lambda (map)
83
+    (declare (dynamic-extent map))
84
+    (extract-key map key)))
85
+
19 86
 (defun-ct regex-match (regex)
20 87
   (lambda (data)
21 88
     (cl-ppcre:scan-to-strings regex data)))
... ...
@@ -38,7 +105,7 @@
38 105
       (funcall key-set
39 106
                (funcall fun key-val)))))
40 107
 
41
-(defun-ct combine (fun1 fun2)
108
+(defun-ct juxt (fun1 fun2)
42 109
   (lambda (item)
43 110
     (list (funcall fun1 item)
44 111
           (funcall fun2 item))))