git.fiddlerwoaroof.com
Browse code

feat(transducer): implement COMPRESSING-RUNS, add one-arity rf

Edward authored on 01/01/2021 01:40:58
Showing 1 changed files
... ...
@@ -27,32 +27,64 @@
27 27
                  exit-early taking dropping transduce
28 28
                  hash-table-builder vector-builder list-builder))
29 29
 
30
-(defmacro define-functional-transducer (name () &body body)
31
-  `(defun ,name (function &rest args)
32
-     (flet ((call-function (it) (apply function it args)))
33
-       (lambda (rf)
34
-         (lambda (acc next)
35
-           ,@body)))))
36
-
37
-(define-functional-transducer mapping ()
38
-  (funcall rf acc (call-function next)))
39
-
40
-(define-functional-transducer mv-mapping ()
41
-  (funcall rf acc (multiple-value-list (call-function next))))
42
-
43
-(define-functional-transducer mv-selecting ()
44
-  (multiple-value-bind (value use-p) (call-function next)
45
-    (if use-p
46
-        (funcall rf acc value)
47
-        acc)))
30
+(defmacro transducer-lambda (&body (((two-arg-acc two-arg-next) &body two-arg-body)
31
+                                    &optional (((one-arg-arg) &body one-arg-body)
32
+                                               '((it) it))))
33
+  (alexandria:with-gensyms (arg1 arg2 next-sym-p)
34
+    `(lambda (,arg1 &optional (,arg2 nil ,next-sym-p))
35
+       (if ,next-sym-p
36
+           (let ((,two-arg-acc ,arg1)
37
+                 (,two-arg-next ,arg2))
38
+             ,@two-arg-body)
39
+           (let ((,one-arg-arg ,arg1))
40
+             ,@one-arg-body)))))
41
+
42
+
43
+(defun mapping (function &rest args)
44
+  (flet ((call-function (it)
45
+           (apply function it args)))
46
+    (lambda (rf)
47
+      (transducer-lambda
48
+        ((acc next)
49
+         (funcall rf acc (call-function next)))
50
+        ((it) (funcall rf it))))))
51
+
52
+(defun mv-mapping (function &rest args)
53
+  (flet ((call-function (it)
54
+           (apply function it args)))
55
+    (lambda (rf)
56
+      (transducer-lambda
57
+        ((acc next)
58
+         (funcall rf acc
59
+                  (multiple-value-list (call-function next))))
60
+        ((it) (funcall rf it))))))
61
+
62
+(defun mv-selecting (function &rest args)
63
+  (flet ((call-function (it)
64
+           (apply function it args)))
65
+    (lambda (rf)
66
+      (transducer-lambda
67
+        ((acc next)
68
+         (multiple-value-bind (value use-p)
69
+             (call-function next)
70
+           (if use-p
71
+               (funcall rf acc value)
72
+               acc)))
73
+        ((it) (funcall rf it))))))
48 74
 
49 75
 (defun hash-table-select (hash-table)
50 76
   (mv-selecting #'gethash hash-table))
51 77
 
52
-(define-functional-transducer filtering ()
53
-  (if (call-function next)
54
-      (funcall rf acc next)
55
-      acc))
78
+(defun filtering (function &rest args)
79
+  (flet ((call-function (it)
80
+           (apply function it args)))
81
+    (lambda (rf)
82
+      (transducer-lambda
83
+        ((acc next)
84
+         (if (call-function next)
85
+             (funcall rf acc next)
86
+             acc))
87
+        ((it) (funcall rf it))))))
56 88
 
57 89
 (defun mv-filtering (function &rest args)
58 90
   (filtering (lambda (it)
... ...
@@ -61,16 +93,45 @@
61 93
 (defun deduping (&optional (test 'eql))
62 94
   (lambda (rf)
63 95
     (let (last)
64
-      (lambda (acc next)
65
-        (prog1 (if (funcall test last next)
66
-                   acc
67
-                   (funcall rf acc next))
68
-          (setf last next))))))
96
+      (transducer-lambda
97
+        ((acc next)
98
+         (prog1 (if (or (null last)
99
+                        (funcall test last next))
100
+                    acc
101
+                    (funcall rf acc next))
102
+           (setf last next)))
103
+        ((it) (funcall rf it))))))
104
+
105
+(defun seq (a b) a b)
106
+(defun compressing-runs (&optional (test 'eql) (combiner 'seq))
107
+  (lambda (rf)
108
+    (let (last leftovers)
109
+      (transducer-lambda
110
+        ((acc next)
111
+         (if (or (null last)
112
+                 (funcall test last next))
113
+             (progn (setf last (funcall combiner last next)
114
+                          leftovers t)
115
+                    acc)
116
+             (progn (prog1 (funcall rf acc last)
117
+                      (setf last next)))))
118
+        ((it)
119
+         (funcall rf
120
+                  (if leftovers
121
+                      (funcall rf it last)
122
+                      it)))))))
123
+
69 124
 
70 125
 (defun catting ()
71 126
   (lambda (rf)
72
-    (lambda (acc next)
73
-      (reduce rf next :initial-value acc))))
127
+    (transducer-lambda
128
+      ((acc next)
129
+       (reduce rf next :initial-value acc))
130
+      ((it) (funcall rf it)))))
131
+
132
+(defun mapcatting (fun)
133
+  (data-lens:• (mapping fun)
134
+               (catting)))
74 135
 
75 136
 (defun splitting (&rest functions)
76 137
   (let ((splitter (apply #'data-lens:juxt functions)))
... ...
@@ -82,27 +143,33 @@
82 143
 (defun taking (n)
83 144
   (lambda (rf)
84 145
     (let ((taken 0))
85
-      (lambda (acc next)
86
-        (incf taken)
87
-        (if (< taken n)
88
-            (funcall rf acc next)
89
-            (exit-early (funcall rf acc next)))))))
146
+      (transducer-lambda
147
+        ((acc next)
148
+         (incf taken)
149
+         (if (<= taken n)
150
+             (funcall rf acc next)
151
+             (exit-early acc)))
152
+        ((it) (funcall rf it))))))
90 153
 
91 154
 (defun dropping (n)
92 155
   (lambda (rf)
93 156
     (let ((taken 0))
94
-      (lambda (acc next)
95
-        (if (< taken n)
96
-            (progn (incf taken)
97
-                   acc)
98
-            (funcall rf acc next))))))
157
+      (transducer-lambda
158
+        ((acc next)
159
+         (if (< taken n)
160
+             (progn (incf taken)
161
+                    acc)
162
+             (funcall rf acc next)))
163
+        ((it) (funcall rf it))))))
99 164
 
100 165
 (defun transduce (xf build seq)
101
-  (unwrap build
102
-          (catch 'done
103
-            (reduce-generic seq
104
-                            (funcall xf (stepper build))
105
-                            (init build)))))
166
+  (let ((transducer (funcall xf (stepper build))))
167
+    (unwrap build
168
+            (funcall transducer
169
+                     (catch 'done
170
+                       (reduce-generic seq
171
+                                       transducer
172
+                                       (init build)))))))
106 173
 (defun eduction (xf seq)
107 174
   (lambda (build)
108 175
     (unwrap
... ...
@@ -115,17 +182,19 @@
115 182
 (defmethod init ((it (eql 'hash-table-builder)))
116 183
   (make-hash-table))
117 184
 (defmethod stepper ((it (eql 'hash-table-builder)))
118
-  (lambda (acc next)
119
-    (destructuring-bind (k v) next
120
-      (setf (gethash k acc) v))
121
-    acc))
185
+  (transducer-lambda
186
+    ((acc next)
187
+     (destructuring-bind (k v) next
188
+       (setf (gethash k acc) v))
189
+     acc)))
122 190
 
123 191
 (defmethod init ((it (eql 'vector-builder)))
124 192
   (make-array 0 :fill-pointer t :adjustable t))
125 193
 (defmethod stepper ((it (eql 'vector-builder)))
126
-  (lambda (acc next)
127
-    (vector-push-extend next acc)
128
-    acc))
194
+  (transducer-lambda
195
+    ((acc next)
196
+     (vector-push-extend next acc)
197
+     acc)))
129 198
 
130 199
 (defmethod init ((it (eql 'list-builder)))
131 200
   (declare (optimize (speed 3)))
... ...
@@ -133,13 +202,14 @@
133 202
     (coerce (vector it it)
134 203
             '(simple-array list (2)))))
135 204
 (defmethod stepper ((it (eql 'list-builder)))
136
-  (lambda (acc a)
137
-    (declare (optimize (speed 3))
138
-             (type (simple-array list (2)) acc))
139
-    (let* ((to-build (elt acc 1)))
140
-      (push a (cdr to-build))
141
-      (setf (elt acc 1) (cdr to-build)))
142
-    acc))
205
+  (transducer-lambda
206
+    ((acc a)
207
+     (declare (optimize (speed 3))
208
+              (type (simple-array list (2)) acc))
209
+     (let* ((to-build (elt acc 1)))
210
+       (push a (cdr to-build))
211
+       (setf (elt acc 1) (cdr to-build)))
212
+     acc)))
143 213
 (defmethod unwrap ((it (eql 'list-builder)) obj)
144 214
   (cdr (elt obj 0)))
145 215