diff options
author | Colin Okay <cbeok@protonmail.com> | 2020-07-13 15:02:27 -0500 |
---|---|---|
committer | Colin Okay <cbeok@protonmail.com> | 2020-07-13 15:02:27 -0500 |
commit | 7472336e62d4ae7f0a2775ae498b376d39aca895 (patch) | |
tree | 6c883710c57d0ec0a39afa7829bcb3d308326fde | |
parent | c168b35f2cf2e176c032ab09309ebcd5614d9f84 (diff) | |
parent | db7d73aef14622717731d05eb13cd667800f33ce (diff) |
Merge branch 'master' of github.com:cbeo/gtwiwtg into experiments
-rw-r--r-- | gtwiwtg.lisp | 89 |
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 ...)" |