Browse code
feat: beginnings of transducers
fiddlerwoaroof authored on 13/12/2020 10:30:08
Showing 3 changed files
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)))) |