Browse code
feat(transduce): build out transducers
fiddlerwoaroof authored on 27/01/2022 23:55:14
Showing 1 changed files
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)) |