aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorColin Okay <cbeok@protonmail.com>2020-07-13 15:02:27 -0500
committerColin Okay <cbeok@protonmail.com>2020-07-13 15:02:27 -0500
commit7472336e62d4ae7f0a2775ae498b376d39aca895 (patch)
tree6c883710c57d0ec0a39afa7829bcb3d308326fde
parentc168b35f2cf2e176c032ab09309ebcd5614d9f84 (diff)
parentdb7d73aef14622717731d05eb13cd667800f33ce (diff)
Merge branch 'master' of github.com:cbeo/gtwiwtg into experiments
-rw-r--r--gtwiwtg.lisp89
1 files changed, 50 insertions, 39 deletions
diff --git a/gtwiwtg.lisp b/gtwiwtg.lisp
index 00534f5..1ce24af 100644
--- a/gtwiwtg.lisp
+++ b/gtwiwtg.lisp
@@ -6,15 +6,15 @@
;; None of the following are meant to be called directly by users of the library.
(defgeneric next (gen)
- (:documentation "Returns the next value of this generator, if
- available. Unspecified behavior if the generator has been exhausted."))
+ (:documentation "Returns the next value of the generator GEN, if
+ available. Unspecified behavior if the GEN has been exhausted."))
(defgeneric has-next-p (gen)
- (:documentation "Returns true if next can be called on the generator"))
+ (:documentation "Returns true if next can be called on the generator GEN."))
(defgeneric stop (gen)
(:documentation "Explicitly stops the generator. Specialize :after
- methods to implement any clean-up that needs to be done when the
+ methods to implement any clean up that needs to be done when the
generator has been consumed."))
;;; Base Generator Class ;;;
@@ -30,7 +30,7 @@
:initform nil
:documentation "Indicates whether or not this generator has been
explicitly stopped. All consumers explicitly stop the generators
- the consume.")))
+ they consume.")))
(defmethod stop ((g generator!))
(setf (stopped-p g) t))
@@ -131,7 +131,7 @@
(defun range (&key (from 0) to (by 1) inclusive)
"Create a generator that produces a series of numbers between FROM
-and TO with step size of BY.
+and TO with a step size of BY.
When INCLUSIVE is non NIL, then TO will be produced by the generator
if it would be the last member of generate series.
@@ -154,8 +154,8 @@ E.g.
(0 3 6 9)
-If TO is NIL, then the generator produces an infinite sequence.
-
+If TO is NIL, then the generator produces an infinite series of
+values.
"
(let ((comparator (if (plusp by)
(if inclusive #'<= #'<)
@@ -172,7 +172,7 @@ If TO is NIL, then the generator produces an infinite sequence.
(defun seq (sequence &key (start 0))
"Turns a sequecne (a list, vector, string, etc) into a
-generator. The resulting generator will generate exactly the memebers
+generator. The resulting generator will generate exactly the members
of the sequence."
(assert (typep sequence 'sequence))
(if (consp sequence)
@@ -183,13 +183,13 @@ of the sequence."
:index (1- start))))
(defun from-thunk-until (thunk &key (until (constantly nil)) clean-up)
- "Creates a generator that produces a series of value by successively
+ "Creates a generator that produces a series of values by successively
calling (FUNCALL THUNK). The iterator stops whenever (FUNCALL UNTIL)
is non null.
-If a CLEAN-UP thunk is supplied, it will be run after consumption of
-the new generator has finished. I.e. when passing this form to a
-consumer such as FOR, FOLD, COLLECT, etc.
+If a CLEAN-UP thunk is supplied, it will be run after the consumption
+of the new generator has finished. (Consumers are forms like FOR,
+COLLECT, FOLD, and so on.)
By default, UNTIL is the function (CONSTANTLY NIL). I.e. it will
generate forever."
@@ -219,13 +219,13 @@ generator, see FROM-THUNK-UNTIL."
(defun from-recurrence (rec n-1 &rest n-m)
"Creates a generator from a recurrence relation.
-REC is a function of K arguments.
+REC is a function of M arguments.
The Nth value of the series generated by the new generator is the result of
-calling REC on the previoius K results.
+calling REC on the previoius M results.
N-1 and N-M are used to initialize the recurrence. (1+ (LENGTH N-M))
-should be K, the number of arguments acepted by REC.
+should be M, the number of arguments acepted by REC.
Example
@@ -244,8 +244,8 @@ Example
(defun repeater (&rest args)
- "Produces a generator that produces an infinite series consisting in
-the values passed as ARGS looped forever."
+ "Creates a generator that produces an infinite series consisting in
+the the values of ARGS looped forever."
(let ((state (copy-list args)))
(from-thunk
(lambda ()
@@ -264,13 +264,16 @@ the values passed as ARGS looped forever."
"Create a generator from a STREAM.
You must supply as STREAM-READER function that accepts the stream as
-its only argument and returns NIL if the stream has run out of input,
+its only argument and returns NIL when the stream has run out of data,
Non-NIL otherwise.
-A quirk is that the last value returned from this generator is NIL.
+The new generator will return NIL as its final generated value..
-Consumers of the new generator will ensure that the stream is properly
-closed..
+Consumers of the new generator (forms like FOR, FOLD, COLLECT, and so
+on) will ensure that the stream is properly closed - you don't need to
+worry. If, however, you create a stream-backed-generator but do not
+actually consume it, then the stream will not be properly closed.
+Always consume your generators by passing them to a consumer!
Here is an example:
@@ -287,13 +290,12 @@ Here is an example:
(defun file-lines (file)
- "Creates a generator that produces the lines of a file. The stream
-to the file is closed when the generator finishes.
-
-FILE is either a path to a file.
+ "Creates a generator that produces the lines of a file. See
+ FROM-INPUT-STREAM for more details about stream-backed-generators.
-Returns NIL on the last iteration.
+FILE is a path to a file.
+The last generated value of the returned generator will be NIL.
"
(from-input-stream (open file)
(lambda (stream) (read-line stream nil nil))))
@@ -302,9 +304,9 @@ Returns NIL on the last iteration.
"Creates a generator that produces the characters of a file. The
stream to the file is closed when the generator finishes.
-FILE is either a path to a file.
+FILE is a path to a file.
-Returns NIL on the last iteration.
+The last generated value of the returned generator will be NIL.
"
(from-input-stream (open file)
(lambda (stream) (read-char stream nil nil))))
@@ -313,9 +315,9 @@ Returns NIL on the last iteration.
"Creates a generator that produces the bytes of a file. The
stream to the file is closed when the generator finishes.
-FILE is either a path to a file.
+FILE is a path to a file.
-Returns NIL on the last iteration.
+The last generated value of the returned generator will be NIL.
"
(from-input-stream (open file :element-type '(unsigned-byte 8))
(lambda (stream) (read-byte stream nil nil))))
@@ -358,12 +360,11 @@ Returns NIL on the last iteration.
(defun map! (map-fn gen &rest gens)
"Maps a function over a number of generators, returning a generator
that produces values that result from calling MAP-FN on those
-generators' elements, in sequence.
+generators' values, in sequence.
The resulting generator will stop producing values as soon as any one
of the source generators runs out of arguments to pass to
-MAP-FN. I.e. The mapped generator is as long as the shortest argument
-generators.
+MAP-FN. I.e. The new generator is as long as the shortest argument.
Error Conditions:
- If any of the generators compare EQL an error will be signalled
@@ -382,8 +383,7 @@ Error Conditions:
(dolist (g all-gens) (stop g))))))
(defun filter! (pred gen)
- "Produces a generator that filters out members of GEN that are NIL
-when applied to PRED.
+ "Creats a generator that generates the values of GEN for which PRED is non null.
Error Condition:
- If GEN has been used elsewhere, an error will be signalled.
@@ -409,13 +409,19 @@ Error Condition:
(stop gen)))))
-(defun inflate! (fn gen)
+(defun inflate! (fn gen &key extra-cleaup)
"FN is expected to be a function that accepts elements of GEN and
returns a new generator.
The generator (INFLATE! FN GEN) generates each element of an
intermediate generator (FN X) for each X generated by GEN.
+When a thunk is supplied to EXTRA-CLEANUP, then that thunk will be
+called when the inflated generator is stopped. EXTRA-CLEANUP exists
+for the case when FN returns generators that are not being created
+within the body of FN, but are merely being \"looked up\" somehow. See
+the implementation of CONCAT! for an example.
+
Here is an example:
> (let ((keys (seq '(:name :occupation :hobbies)))
@@ -457,7 +463,8 @@ Error Conditions:
:clean-up
(lambda ()
(stop gen)
- (when sub-gen (stop sub-gen)))))))
+ (when sub-gen (stop sub-gen))
+ (when extra-cleanup (funcall extra-cleanup)))))))
(defun concat! (gen &rest gens)
@@ -469,7 +476,11 @@ Error Conditions:
- If any of the generators has been used elsewhere, an error will be sigalled.
"
(sully-when-clean (list* gen gens))
- (inflate! #'identity (seq (list* gen gens))))
+ (inflate! #'identity (seq (list* gen gens))
+ ;; in the case that not all arguments are consumed,
+ ;; explicitly stop each one at clean-up time.
+ :extra-cleanup (lambda ()
+ (dolist (g (list* gen gens)) (stop g)))))
(defun zip! (gen &rest gens)
"Is a shortcut for (MAP! #'LIST GEN1 GEN2 ...)"