git.fiddlerwoaroof.com
Browse code

feat: beginnings of transducers

fiddlerwoaroof authored on 13/12/2020 10:30:08
Showing 3 changed files
... ...
@@ -12,3 +12,16 @@
12 12
   :components ((:file "package")
13 13
                (:file "optics")
14 14
                (:file "lens")))
15
+
16
+(asdf:defsystem #:data-lens/beta/transducer
17
+  :description #.(format nil "~a ~a ~a"
18
+                         "Utilities for building data transormations from"
19
+                         "composable functions, modeled on lenses and"
20
+                         "transducers")
21
+  :author "Edward Langley <el-cl@elangley.org>"
22
+  :license "MIT"
23
+  :depends-on (:data-lens
24
+               :alexandria)
25
+  :serial t
26
+  :components ((:file "package")
27
+               (:file "transducer")))
... ...
@@ -20,3 +20,9 @@
20 20
            #:maximizing #:zipping #:applying #:splice-elt #:transform-elt #:denest
21 21
            #:op #:defalias #:<> #:<>1 #:== #:•
22 22
            ))
23
+
24
+(defpackage :data-lens.transducers.beta
25
+  (:use :cl)
26
+  (:export #:mapping :filtering :deduping :catting :splitting
27
+           #:exit-early :taking :dropping :transduce
28
+           #:hash-table-builder :vector-builder :list-builder))
23 29
new file mode 100644
... ...
@@ -0,0 +1,108 @@
1
+(in-package :data-lens.transducers.beta)
2
+
3
+(defun mapping (function)
4
+  (lambda (rf)
5
+    (lambda (acc next)
6
+      (funcall rf acc (funcall function next)))))
7
+
8
+(defun filtering (predicate)
9
+  (lambda (rf)
10
+    (lambda (acc next)
11
+      (if (funcall predicate next)
12
+          (funcall rf acc next)
13
+          acc))))
14
+
15
+(defun deduping (&optional (test 'eql))
16
+  (lambda (rf)
17
+    (let (last)
18
+      (lambda (acc next)
19
+        (prog1 (if (funcall test last next)
20
+                   acc
21
+                   (funcall rf acc next))
22
+          (setf last next))))))
23
+
24
+(defun catting ()
25
+  (lambda (rf)
26
+    (lambda (acc next)
27
+      (reduce rf next :initial-value acc))))
28
+
29
+(defun splitting (&rest functions)
30
+  (let ((splitter (apply #'data-lens:juxt functions)))
31
+    (mapping splitter)))
32
+
33
+(defun exit-early (acc)
34
+  (throw 'done acc))
35
+
36
+(defun taking (n)
37
+  (lambda (rf)
38
+    (let ((taken 0))
39
+      (lambda (acc next)
40
+        (incf taken)
41
+        (if (< taken n)
42
+            (funcall rf acc next)
43
+            (exit-early (funcall rf acc next)))))))
44
+
45
+(defun dropping (n)
46
+  (lambda (rf)
47
+    (let ((taken 0))
48
+      (lambda (acc next)
49
+        (if (< taken n)
50
+            (progn (incf taken)
51
+                   acc)
52
+            (funcall rf acc next))))))
53
+
54
+(defun transduce (xf build seq)
55
+  (funcall build
56
+           (catch 'done
57
+             (reduce (funcall xf build) seq :initial-value (funcall build)))))
58
+
59
+(defmacro comment (&body body)
60
+  (declare (ignore body))
61
+  nil)
62
+
63
+(defun hash-table-builder (&optional (acc nil acc-p) (next nil next-p))
64
+  (cond (next-p (destructuring-bind (k v) next
65
+                  (setf (gethash k acc) v)) acc)
66
+        (acc-p acc)
67
+        (t (make-hash-table))))
68
+
69
+(defun vector-builder (&optional (acc nil acc-p) (next nil next-p))
70
+  (cond (next-p (vector-push-extend next acc) acc)
71
+        (acc-p acc)
72
+        (t (make-array 0 :fill-pointer t :adjustable t))))
73
+
74
+(eval-when (:load-toplevel :compile-toplevel :execute)
75
+  (labels ((make-snoc ()
76
+             (vector nil nil))
77
+           (add-to-snoc (acc a)
78
+             (if (elt acc 1)
79
+                 (let* ((to-build (elt acc 1)))
80
+                   (push a (cdr to-build))
81
+                   (setf (elt acc 1) (cdr to-build)))
82
+                 (let ((new (list a)))
83
+                   (setf (elt acc 0) new
84
+                         (elt acc 1) new)))
85
+             acc)
86
+           (desnoc (acc)
87
+             (elt acc 0)))
88
+    (defun list-builder (&optional (acc nil acc-p) (next nil next-p))
89
+      (cond (next-p (add-to-snoc acc next))
90
+            (acc-p (desnoc acc))
91
+            (t (make-snoc))))))
92
+
93
+(comment
94
+  (defun 2* (it)
95
+    (* 2 it))
96
+
97
+  (let ((result (transduce (alexandria:compose (catting)
98
+                                               (mapping #'parse-integer)
99
+                                               (filtering (complement #'evenp))
100
+                                               (mapping (data-lens:juxt #'identity #'identity))
101
+                                               (mapping (data-lens:transform-head #'2*))
102
+                                               (mapping (data-lens:transform-head #'1+))
103
+                                               (taking 2))
104
+                           'hash-table-builder
105
+                           '(("123" "234" "345" "454") ("568" "490") ("567" "213")))
106
+                ))
107
+    (values result
108
+            (alexandria:hash-table-alist result))))