git.fiddlerwoaroof.com
Browse code

feat(transduce): build out transducers

fiddlerwoaroof authored on 27/01/2022 23:55:14
Showing 1 changed files
... ...
@@ -1,16 +1,5 @@
1
-(defun compose (function &rest more-functions)
2
-  "Returns a function composed of FUNCTION and MORE-FUNCTIONS that applies its
3
-arguments to to each in turn, starting from the rightmost of MORE-FUNCTIONS,
4
-and then calling the next one with the primary value of the last."
5
-  (declare (optimize (speed 3) (safety 1) (debug 1)))
6
-  (reduce (lambda (f g)
7
-	          (let ((f (ensure-function f))
8
-		              (g (ensure-function g)))
9
-	            (lambda (&rest arguments)
10
-		            (declare (dynamic-extent arguments))
11
-		            (funcall f (apply g arguments)))))
12
-          more-functions
13
-          :initial-value function))
1
+(ql:quickload :alexandria)
2
+(import 'alexandria:compose)
14 3
 
15 4
 (defun make-snoc ()
16 5
   (vector nil nil))
... ...
@@ -25,10 +14,8 @@ and then calling the next one with the primary value of the last."
25 14
   acc)
26 15
 (defun desnoc (acc)
27 16
   (elt acc 0))
28
-
29 17
 (defun 2* (it)
30 18
   (* 2 it))
31
-
32 19
 ;; mapcar: conses up three lists, but keeps the steps separate
33 20
 cl-user> (mapcar '1+
34 21
                  (mapcar '2*
... ...
@@ -230,7 +217,7 @@ cl-user> (labels ((mapping (function)
230 217
 #<HASH-TABLE :TEST EQL :COUNT 4 {10075E2E13}>
231 218
 ((427 . 213) (1135 . 567) (691 . 345) (469 . 234))
232 219
 
233
-;; We can trivially switch data structures now
220
+;;; without EXIT-EARLY:
234 221
 cl-user> (labels ((mapping (function)
235 222
                     (lambda (rf)
236 223
                       (lambda (acc next)
... ...
@@ -245,22 +232,90 @@ cl-user> (labels ((mapping (function)
245 232
                     (lambda (rf)
246 233
                       (lambda (acc next)
247 234
                         (reduce rf next :initial-value acc))))
235
+                  (exit-early (acc)
236
+                    (throw 'done acc))
237
+                  (taking (n)
238
+                    (let ((taken 0))
239
+                      (lambda (rf)
240
+                        (lambda (acc next)
241
+                          (format t "~&>>> ~s~%" next)
242
+                          (if (< taken n)
243
+                              (prog1 (funcall rf acc next)
244
+                                (incf taken))
245
+                              acc)))))
248 246
                   (transduce (xf build seq)
249 247
                     (funcall build
250
-                             (reduce (funcall xf build) seq :initial-value (funcall build)))))
248
+                             (catch 'done
249
+                               (reduce (funcall xf build) seq :initial-value (funcall build))))))
251 250
            (let ((result (transduce (compose (catting)
252 251
                                              (mapping #'parse-integer)
253 252
                                              (filtering (complement #'evenp))
254 253
                                              (mapping (data-lens:juxt #'identity #'identity))
255 254
                                              (mapping (data-lens:transform-head #'2*))
256
-                                             (mapping (data-lens:transform-head #'1+)))
255
+                                             (mapping (data-lens:transform-head #'1+))
256
+                                             (taking 2))
257
+                                    (lambda (&optional (acc nil acc-p) (next nil next-p))
258
+                                      (cond (next-p (destructuring-bind (k v) next
259
+                                                      (setf (gethash k acc) v)) acc)
260
+                                            (acc-p acc)
261
+                                            (t (make-hash-table))))
262
+                                    '(("123" "234" "345" "454") ("568" "490") ("567" "213")))))
263
+             (values result
264
+                     (alexandria:hash-table-alist result))))
265
+;; >>> (247 123)
266
+;; >>> (691 345)
267
+;; >>> (1135 567)
268
+;; >>> (427 213)
269
+;; #<HASH-TABLE :TEST EQL :COUNT 2 {101585B6B3}>
270
+;; ((691 . 345) (247 . 123))
271
+
272
+
273
+;;; with EXIT-EARLY:
274
+cl-user> (labels ((mapping (function)
275
+                    (lambda (rf)
276
+                      (lambda (acc next)
277
+                        (funcall rf acc (funcall function next)))))
278
+                  (filtering (predicate)
279
+                    (lambda (rf)
280
+                      (lambda (acc next)
281
+                        (if (funcall predicate next)
282
+                            (funcall rf acc next)
283
+                            acc))))
284
+                  (catting ()
285
+                    (lambda (rf)
286
+                      (lambda (acc next)
287
+                        (reduce rf next :initial-value acc))))
288
+                  (exit-early (acc)
289
+                    (throw 'done acc))
290
+                  (taking (n)
291
+                    (let ((taken 0))
292
+                      (lambda (rf)
293
+                        (lambda (acc next)
294
+                          (format t "~&>>> ~s~%" next)
295
+                          (incf taken)
296
+                          (if (< taken n)
297
+                              (funcall rf acc next)
298
+                              (exit-early (funcall rf acc next)))))))
299
+                  (transduce (xf build seq)
300
+                    (funcall build
301
+                             (catch 'done
302
+                               (reduce (funcall xf build) seq :initial-value (funcall build))))))
303
+           (let ((result (transduce (compose (catting)
304
+                                             (mapping #'parse-integer)
305
+                                             (filtering (complement #'evenp))
306
+                                             (mapping (data-lens:juxt #'identity #'identity))
307
+                                             (mapping (data-lens:transform-head #'2*))
308
+                                             (mapping (data-lens:transform-head #'1+))
309
+                                             (taking 2))
257 310
                                     (lambda (&optional (acc nil acc-p) (next nil next-p))
258 311
                                       (cond (next-p (destructuring-bind (k v) next
259 312
                                                       (setf (gethash k acc) v)) acc)
260 313
                                             (acc-p acc)
261 314
                                             (t (make-hash-table))))
262
-                                    '(("234" "345") ("567" "213")))))
315
+                                    '(("123" "234" "345" "454") ("568" "490") ("567" "213")))))
263 316
              (values result
264 317
                      (alexandria:hash-table-alist result))))
265
-#<HASH-TABLE :TEST EQL :COUNT 3 {100AA69173}>
266
-((427 . 213) (1135 . 567) (691 . 345))
318
+;; >>> (247 123)
319
+;; >>> (691 345)
320
+;; #<HASH-TABLE :TEST EQL :COUNT 2 {1015B46E23}>
321
+;; ((691 . 345) (247 . 123))