-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerators.lisp
64 lines (52 loc) · 1.52 KB
/
generators.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
(defpackage :aoc.generator
(:use :cl)
(:export #:stop
#:next
#:on-stop
#:iterate
#:sum
#:fold-generator
#:all-lines
#:group-by-n
#:size
#:keep-if))
(in-package :aoc.generator)
(define-condition stop () ())
(defun stop ()
(signal 'stop))
(defgeneric next (generator &rest args)
(:method (_ &rest args)
(declare (ignore args))
(stop))
(:method ((f function) &rest args) (apply f args)))
(defun all-lines (stream)
(lambda ()
(let ((line (read-line stream nil stream)))
(if (eq line stream)
(stop)
line))))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defmacro on-stop (form &body body)
(alexandria:with-gensyms (_)
`(handler-bind ((stop (lambda (,_) (declare (ignore ,_)) ,form)))
,@body))))
(defun iterate (generator fn)
(loop
(on-stop (return)
(funcall fn (next generator)))))
(defun fold-generator (folder accumulator generator)
(loop
(on-stop (return accumulator)
(setf accumulator (funcall folder accumulator (next generator))))))
(defun sum (generator)
(fold-generator #'+ 0 generator))
(defun size (generator)
(fold-generator (lambda (acc _) (declare (ignore _)) (1+ acc)) 0 generator))
(defun group-by-n (n generator)
(lambda () (loop repeat n collect (next generator))))
(defun keep-if (predicate generator)
(lambda ()
(loop
(let ((v (next generator)))
(when (funcall predicate v)
(return v))))))