Browse code
Add some useful utilities and an example
Ed Langley authored on 18/08/2018 19:55:10
Showing 2 changed files
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)))) |