diff --git a/.gitignore b/.gitignore index 7e2fb76..4263603 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.fas vend deps.dot +vendored/java/ +*.abcl diff --git a/src/abcl.lisp b/src/abcl.lisp new file mode 100644 index 0000000..5f41445 --- /dev/null +++ b/src/abcl.lisp @@ -0,0 +1,154 @@ +;;; Integrations custom to ABCL and the provisioning of JVM packages from Maven +;;; Central. Compatible with the syntax expected by `abcl-asdf', namely: +;;; +;;; ``` +;;; (asdf:defsystem :log4j +;;; :components ((:mvn "log4j/log4j" :version "1.4.9")) +;;; ``` +;;; +;;; Many functions here thus operate on a `dep' plist of the shape: +;;; +;;; ``` +;;; (:group ... :artifact ... :version ...) +;;; ``` + +(in-package :vend) + +(defun maven-deps (system) + "Given a `defsystem' sexp, extract its Maven Central dependencies, if any." + (t:transduce + (t:comp (t:filter (lambda (pl) (and (getf pl :mvn) (getf pl :version)))) + (t:map (lambda (pl) + (destructuring-bind (group artifact) + (t::string-split (getf pl :mvn) :separator #\/) + (list :group group + :artifact artifact + :version (getf pl :version)))))) + #'t:cons + (getf system :components))) + +#+nil +(maven-deps '(asdf:defsystem :log4j + :components ((:mvn "log4j/log4j" :version "1.4.9")))) + +(defun jar-download-command (dep) + "Produce the command necessary to download something from Maven Central." + (list "mvn" "dependency:get" + (format nil "-DgroupId=~a" (getf dep :group)) + (format nil "-DartifactId=~a" (getf dep :artifact)) + (format nil "-Dversion=~a" (getf dep :version)) + "-Dmaven.repo.local=vendored/java/")) + +#+nil +(jar-download-command + (car (maven-deps '(asdf:defsystem :log4j + :components ((:mvn "log4j/log4j" :version "1.4.9")))))) + +(defun download-from-maven (dep) + (let ((cmd (jar-download-command dep))) + (multiple-value-bind (stream code obj) + (ext:run-program (car cmd) (cdr cmd) :output *standard-output*) + (declare (ignore stream obj)) + (assert (= 0 code) nil "Pulling ~a from Maven Central failed" (getf dep :mvn))))) + +#+nil +(download-from-maven '(:mvn "org.apache.commons/commons-text" :version "1.13.1")) + +(defun java-dep-dir (dep) + "Rederive a directory path in which a JAR and POM can be found." + (let* ((group (getf dep :group)) + (artifact (getf dep :artifact)) + (version (getf dep :version)) + (parts (t::string-split group :separator #\.))) + (p:ensure-directory (apply #'p:join "vendored" "java" (append parts (list artifact version)))))) + +#+nil +(java-dep-dir '(:group "org.apache.commons" :artifact "commons-text" :version "1.13.1")) + +(defun java-jar-path (dep) + "The expected path to a downloaded JAR of this dep." + (let ((jar (format nil "~a-~a.jar" (getf dep :artifact) (getf dep :version)))) + (p:join (java-dep-dir dep) jar))) + +#+nil +(java-jar-path '(:group "org.apache.commons" :artifact "commons-text" :version "1.13.1")) + +(defun java-pom-path (dep) + "The expected path to a downloaded POM of this dep." + (let ((pom (format nil "~a-~a.pom" (getf dep :artifact) (getf dep :version)))) + (p:join (java-dep-dir dep) pom))) + +#+nil +(java-pom-path '(:group "org.apache.commons" :artifact "commons-text" :version "1.13.1")) + +;; NOTE: 2025-05-25 This is about 2x faster than `uiop:read-file-string', and +;; much, much faster than reading it in line-by-line and re-fusing via +;; transducers. +(declaim (ftype (function (pathname) (simple-array character *)) string-from-file)) +(defun string-from-file (path) + "Read some given file into a single string." + (with-open-file (stream path :direction :input :element-type 'character) + (let* ((len (file-length stream)) + (str (make-string len))) + (read-sequence str stream) + str))) + +#+nil +(x:parse (string-from-file (java-pom-path '(:group "org.apache.commons" :artifact "commons-text" :version "1.13.1")))) + +(defun deps-from-xml (xml) + "Extract the non-test dependencies from some parsed XML." + (let* ((content (x:content xml)) + (deps (gethash "dependency" (x:content (gethash "dependencies" content)))) + (props (x:content (gethash "properties" content)))) + (t:transduce + (t:comp (t:map #'x:content) + (t:filter (lambda (ht) (gethash "version" ht))) + (t:filter (lambda (ht) (not (gethash "scope" ht)))) + (t:map (lambda (ht) + (list :group (x:content (gethash "groupId" ht)) + :artifact (x:content (gethash "artifactId" ht)) + :version (dep-version props ht))))) + #'t:cons + deps))) + +#+nil +(let* ((path (java-pom-path '(:group "org.apache.commons" :artifact "commons-text" :version "1.13.1"))) + (xml (x:parse (string-from-file path)))) + (deps-from-xml xml)) + +(defun dep-version (props dep) + "For a particular dependency, discover its true version." + (let ((ver (x:content (gethash "version" dep)))) + (if (eql #\$ (schar ver 0)) + (x:content (gethash (extract-prop-name ver) props)) + ver))) + +(defun extract-prop-name (s) + "Pull the inner property name from a ${} fence." + (let ((len (length s))) + (subseq s 2 (1- len)))) + +#+nil +(extract-prop-name "${commons.lang3.version}") + +(defun classpath (dep) + "Build a collection of names matched to JAR locations on disk." + (labels ((recurse (ht curr) + ;; FIXME: 2025-05-25 This condition may be incorrect. It might be + ;; better to both the group and artifact together as the key. + (let ((name (getf curr :artifact))) + (if (gethash name ht) + ht + (let* ((jar (java-jar-path curr)) + (pom (java-pom-path curr)) + (xml (x:parse (string-from-file pom)))) + (setf (gethash name ht) jar) + (dolist (dep (deps-from-xml xml)) + (recurse ht dep)) + ht))))) + (recurse (make-hash-table :test #'equal :size 64) dep))) + +#+nil +(classpath '(:group "org.apache.commons" :artifact "commons-text" :version "1.13.1")) + diff --git a/src/asd.lisp b/src/asd.lisp index a7d747d..da3758d 100644 --- a/src/asd.lisp +++ b/src/asd.lisp @@ -74,7 +74,7 @@ #++ (chipz? "#+chipz-system:gray-streams") -(defun string-from-file (path) +(defun string-from-asd-file (path) "Preserves newlines but removes whole-line comments." (t:transduce (t:comp (t:filter (lambda (line) (not (comment? line)))) (t:filter (lambda (line) (not (chipz? (string-left-trim " " line))))) @@ -83,7 +83,7 @@ #'t:string path)) #++ -(string-from-file #p"vend.asd") +(string-from-asd-file #p"vend.asd") (defun systems-from-file (path) "Extract all `defsystem' forms as proper sexp from a file." @@ -91,7 +91,7 @@ (let* ((clean (sanitize sys)) (stream (make-string-input-stream clean))) (read stream nil :eof)))) - #'t:cons (all-system-strings (string-from-file path)))) + #'t:cons (all-system-strings (string-from-asd-file path)))) #++ (systems-from-file (car (asd-files "./"))) diff --git a/src/package.lisp b/src/package.lisp index 2db5f1c..343acb8 100644 --- a/src/package.lisp +++ b/src/package.lisp @@ -2,7 +2,8 @@ (:use :cl) (:local-nicknames (#:g #:simple-graph) (#:p #:filepaths) - (#:t #:transducers)) + (#:t #:transducers) + (#:x #:parcom/xml)) (:export #:main) (:documentation "Simply vendor your Common Lisp project dependencies.")) diff --git a/src/registry.lisp b/src/registry.lisp index 103df2e..143695b 100644 --- a/src/registry.lisp +++ b/src/registry.lisp @@ -127,6 +127,8 @@ map back to the parent, such that later only one git clone is performed.") "Repositories marked as deprecated or archived by their authors.") ;; TODO: 2024-01-11 Make this a HashTable. +;; +;; 2025-05-26 Maybe. (defparameter +sources+ '(:3b-bmfont "https://github.com/3b/3b-bmfont.git" :3b-hdr "https://github.com/3b/3b-hdr.git" @@ -135,6 +137,7 @@ map back to the parent, such that later only one git clone is performed.") :3d-math "https://github.com/Shinmera/3d-math.git" :3d-spaces "https://github.com/Shirakumo/3d-spaces.git" :40ants-doc "https://github.com/40ants/doc.git" + :abcl-memory-compiler "https://github.com/alejandrozf/abcl-memory-compiler.git" :access "https://github.com/AccelerationNet/access.git" :acclimation "https://github.com/robert-strandh/Acclimation.git" :action-list "https://github.com/Shinmera/action-list.git" diff --git a/vend.asd b/vend.asd index 46a6660..99503d0 100644 --- a/vend.asd +++ b/vend.asd @@ -3,11 +3,12 @@ :author "Colin Woodbury " :license "MPL-2.0" :homepage "https://github.com/fosskers/vend" - :depends-on (:filepaths :simple-graph :transducers) + :depends-on (:filepaths :simple-graph :transducers :parcom/xml) :serial t :components ((:module "src" :components ((:file "package") (:file "registry") + (:file "abcl") (:file "asd") (:file "vend")))) :description "Simply vendor your Common Lisp project dependencies.") diff --git a/vendored/transducers/CHANGELOG.md b/vendored/transducers/CHANGELOG.md index e8745ee..095f531 100644 --- a/vendored/transducers/CHANGELOG.md +++ b/vendored/transducers/CHANGELOG.md @@ -2,9 +2,21 @@ ### Unreleased +#### Fixed + +- A forgotten `all?` export. + +### 1.4.0 (2025-02-15) + #### Added - `unique-by` for more control over how uniqueness is determined. +- `for` as a better pattern for doing something effectful over the stream. +- `any?`, `all?`, and `reduced?` as modern aliases. + +#### Deprecated + +- `for-each`: use `for` instead. ### 1.3.1 (2025-01-13) diff --git a/vendored/transducers/README.org b/vendored/transducers/README.org index 0ceffb3..c3cc4c9 100644 --- a/vendored/transducers/README.org +++ b/vendored/transducers/README.org @@ -38,7 +38,61 @@ evenly-lengthed lines./ #+RESULTS: : 10696 -Looking for Transducers in other Lisps? Check out the [[https://codeberg.org/fosskers/transducers.el][Emacs Lisp]] and [[https://git.sr.ht/~fosskers/transducers.fnl][Fennel]] implementations! +Looking for Transducers in other Lisps? Check out the [[https://github.com/fosskers/transducers.el][Emacs Lisp]] and [[https://github.com/fosskers/transducers.fnl][Fennel]] implementations! + +* Table of Contents :TOC_5_gh:noexport: +- [[#compatibility][Compatibility]] +- [[#history-and-motivation][History and Motivation]] +- [[#installation][Installation]] +- [[#usage-and-theory][Usage and Theory]] + - [[#importing][Importing]] + - [[#transducers-reducers-and-sources][Transducers, Reducers, and Sources]] + - [[#processing-json-data][Processing JSON Data]] + - [[#fset-immutable-collections][Fset: Immutable Collections]] +- [[#api][API]] + - [[#transducers][Transducers]] + - [[#pass-map][pass, map]] + - [[#filter-filter-map-unique-unique-by-dedup][filter, filter-map, unique, unique-by, dedup]] + - [[#drop-drop-while-take-take-while][drop, drop-while, take, take-while]] + - [[#uncons-concatenate-flatten][uncons, concatenate, flatten]] + - [[#segment-window-group-by][segment, window, group-by]] + - [[#intersperse-enumerate-step-scan][intersperse, enumerate, step, scan]] + - [[#once][once]] + - [[#log][log]] + - [[#from-csv-into-csv][from-csv, into-csv]] + - [[#reducers][Reducers]] + - [[#cons-snoc-vector-string-hash-table][cons, snoc, vector, string, hash-table]] + - [[#count-average-median][count, average, median]] + - [[#any-all][any?, all?]] + - [[#first-last-find][first, last, find]] + - [[#fold][fold]] + - [[#for][for]] + - [[#sources][Sources]] + - [[#ints-random][ints, random]] + - [[#cycle-repeat-shuffle][cycle, repeat, shuffle]] + - [[#plist][plist]] + - [[#reversed][reversed]] + - [[#utilities][Utilities]] + - [[#comp-const][comp, const]] + - [[#reduced-reduced-reduced-val][reduced, reduced?, reduced-val]] +- [[#example-gallery][Example Gallery]] + - [[#reading-lines-from-a-file][Reading lines from a File]] + - [[#reducing-into-property-lists-and-assocation-lists][Reducing into Property Lists and Assocation Lists]] + - [[#json-calculating-average-age][JSON: Calculating average age]] + - [[#sieve-of-eratosthenes][Sieve of Eratosthenes]] +- [[#writing-your-own-primitives][Writing your own Primitives]] + - [[#transducers-1][Transducers]] + - [[#map---a-simple-transformation][map - A simple transformation]] + - [[#filter---ignoring-input][filter - Ignoring input]] + - [[#take-while---short-circuiting][take-while - Short-circuiting]] + - [[#unique---stateful-transduction][unique - Stateful transduction]] + - [[#reducers-1][Reducers]] + - [[#count---simple-cumulative-state][count - Simple cumulative state]] + - [[#cons---some-post-processing][cons - Some post-processing]] + - [[#anyp---short-circuiting][anyp - Short-circuiting]] + - [[#sources-1][Sources]] +- [[#limitations][Limitations]] +- [[#resources][Resources]] * Compatibility @@ -47,13 +101,14 @@ Looking for Transducers in other Lisps? Check out the [[https://codeberg.org/fos | Compiler | Compiles? | Tests? | Notes | |-----------+-----------+--------+----------------------------| -| SBCL | ✅ | ✅ | | -| ECL | ✅ | ✅ | | -| Clasp | ✅ | - | | -| ABCL | ✅ | ❌ | No [[https://en.wikipedia.org/wiki/Tail_call][TCO]] in recursive =labels= | -| CCL | ✅ | ✅ | | -| Allegro | ✅ | - | | -| LispWorks | ✅ | - | | +| SBCL | ✅ | ✅ | | +| ECL | ✅ | ✅ | | +| Clasp | ✅ | - | | +| ABCL | ✅ | ❌ | [[https://github.com/armedbear/abcl/issues/675][No TCO]] in recursive =labels= | +| CCL | ✅ | ✅ | | +| [[https://gitlab.com/gnu-clisp/clisp][Clisp]] | ✅ | - | Supports TCO but not [[https://gitlab.com/gnu-clisp/clisp/-/merge_requests/3][PLN]] | +| Allegro | ✅ | ✅ | | +| LispWorks | ✅ | - | | [[https://wiki.archlinux.org/title/Common_Lisp#Historical][Historical implementations]] are not considered. @@ -70,18 +125,7 @@ while adding many other convenient operations commonly found in other languages. * Installation -This library is available on [[https://quickdocs.org/cl-transducers][Quicklisp]] and [[https://ultralisp.org/projects/fosskers/cl-transducers][Ultralisp]]. To download the main -system: - -#+begin_src lisp -(ql:quickload :transducers) -#+end_src - -For the JSON extensions: - -#+begin_src lisp -(ql:quickload :transducers/jzon) -#+end_src +This library is available through [[https://github.com/fosskers/vend][vend]], as well as [[https://quickdocs.org/cl-transducers][Quicklisp]] and [[https://ultralisp.org/projects/fosskers/cl-transducers][Ultralisp]]. * Usage and Theory @@ -641,25 +685,25 @@ median is extracted. #+RESULTS: : 1 -*** anyp, allp +*** any?, all? -Yield t if any element in the transduction satisfies PRED. Short-circuits the +Yield =t= if any element in the transduction satisfies PRED. Short-circuits the transduction as soon as the condition is met. #+begin_src lisp :results verbatim :exports both (in-package :transducers) -(transduce #'pass (anyp #'evenp) '(1 3 5 7 9 2)) +(transduce #'pass (any? #'evenp) '(1 3 5 7 9 2)) #+end_src #+RESULTS: : T -Yield t if all elements of the transduction satisfy PRED. Short-circuits with +Yield =t= if all elements of the transduction satisfy PRED. Short-circuits with NIL if any element fails the test. #+begin_src lisp :results verbatim :exports both (in-package :transducers) -(transduce #'pass (allp #'oddp) '(1 3 5 7 9)) +(transduce #'pass (all? #'oddp) '(1 3 5 7 9)) #+end_src #+RESULTS: @@ -731,14 +775,14 @@ With a seed: In Clojure this function is called =completing=. -*** for-each +*** for Run through every item in a transduction for their side effects. Throws away all -results and yields t. +results and yields a final =t=. #+begin_src lisp :results verbatim :exports both (in-package :transducers) -(transduce (map (lambda (n) (format t "~a~%" n))) #'for-each #(1 2 3 4)) +(transduce (map #'1+) (for (lambda (n) (format t "~a~%" n))) #(1 2 3 4)) #+end_src #+RESULTS: @@ -900,7 +944,7 @@ Return a function that ignores its argument and returns ITEM instead. #+RESULTS: : 108 -*** reduced, reduced-p, reduced-val +*** reduced, reduced?, reduced-val When writing your own transducers and reducers, these functions allow you to short-circuit the entire operation. @@ -918,7 +962,7 @@ Here is a simplified definition of ~first~: You can see ~reduced~ being used to wrap the return value. ~transduce~ sees this wrapping and immediately halts further processing. -~reduced-p~ and ~reduced-val~ can similarly be used (mostly within transducer +~reduced?~ and ~reduced-val~ can similarly be used (mostly within transducer functions) to check if some lower transducer (or the reducer) has signaled a short-circuit, and if so potentially perform some clean-up. This is important for transducers that carry internal state. @@ -1009,7 +1053,7 @@ optionally continues by calling the next function in the chain, or it ignores the current input, or it short-circuits the stream entirely. We'll see examples of all of these below. -*** =map= - A simple transformation +*** map - A simple transformation Here is how =map= is implemented in the library. Let's study it to learn the overall structure of transducers in general. @@ -1067,7 +1111,7 @@ Common Lisp cannot. If the =i-p= test fails, then we know the transduction is ov now, just know that this is the last thing that the top-level =transduce= call attempts as it is finalising the result. -*** =filter= - Ignoring input +*** filter - Ignoring input With =map= fresh in your mind, now stare at this: @@ -1091,7 +1135,7 @@ given_ and directly return, going no further for this particular input element. Then, =transduce= will supply the next one. The effect is what we'd expect of =filter=; some elements make it through the stream and some don't. -*** =take-while= - Short-circuiting +*** take-while - Short-circuiting Similar to =filter= is =take-while=, except that the latter halts the stream entirely as soon as an element fails the predicate. @@ -1113,7 +1157,7 @@ Here =reduced= makes its debut. This wraps the given value in a special type tha signals to =transduce= that the transduction has been short-circuited and must end. Nothing further will be pulled from the Source. -*** =unique= - Stateful transduction +*** unique - Stateful transduction Despite just being a group of composed functions, individual transducers can hold state. Consider =unique=, which is called like: @@ -1159,7 +1203,7 @@ element already. A Reducer is a function that _consumes a stream_. It accepts two, one, or no arguments. -*** =count= - Simple cumulative state +*** count - Simple cumulative state An example of =count= being called: @@ -1198,7 +1242,7 @@ Reducer with the cumulative state thusfar and the current stream element. The Reducer then decides what to do with them. In the case of =count=, the element itself is ignored and we just add 1 to our growing =acc=. -*** =cons= - Some post-processing +*** cons - Some post-processing #+begin_src lisp (defun cons (&optional (acc nil a-p) (input nil i-p)) @@ -1218,7 +1262,7 @@ reversed once before being yielded to the user. In (M) our "zero value" is the empty list. Otherwise, what would we be consing onto on the first pass of (I)? -*** =anyp= - Short-circuiting +*** anyp - Short-circuiting =anyp= stops as soon as anything satisfies its predicate. @@ -1306,7 +1350,7 @@ Now come a trio of functions that drive the iteration: (if (< i 0) acc ;; (4) We're done. (let ((acc (safe-call f acc (aref vec i)))) ;; (5) "Safe" application of the transducer chain. - (if (reduced-p acc) ;; (6a) Short-circuiting occured. Time to go home. + (if (reduced? acc) ;; (6a) Short-circuiting occured. Time to go home. (reduced-val acc) (recurse acc (1- i))))))) ;; (6b) Otherwise, keep going. (recurse identity (1- len))))) diff --git a/vendored/transducers/transducers.asd b/vendored/transducers/transducers.asd index 788172f..98cc6e8 100644 --- a/vendored/transducers/transducers.asd +++ b/vendored/transducers/transducers.asd @@ -1,5 +1,5 @@ (defsystem "transducers" - :version "1.3.1" + :version "1.4.0" :author "Colin Woodbury " :license "MPL-2.0" :depends-on () @@ -7,10 +7,47 @@ :components ((:module "transducers" :components ((:file "package") + (:file "deprecated") (:file "utils") (:file "transducers") (:file "reducers") (:file "sources") (:file "entry") (:file "conditions")))) - :description "Ergonomic, efficient data processing.") + :description "Ergonomic, efficient data processing." + :in-order-to ((test-op (test-op :transducers/tests)))) + +(defsystem "transducers/jzon" + :version "1.4.0" + :author "Colin Woodbury " + :license "MPL-2.0" + :depends-on (:transducers :com.inuoe.jzon :trivia) + :components ((:module "jzon" + :components + ((:file "jzon")))) + :description "JSON extension for Transducers.") + +(defsystem "transducers/fset" + :version "1.4.0" + :author "Colin Woodbury " + :license "MPL-2.0" + :depends-on (:transducers :fset) + :components ((:module "fset" + :components + ((:file "fset")))) + :description "Fset extension for Transducers.") + +(defsystem "transducers/tests" + :author "Colin Woodbury " + :license "MPL-2.0" + :depends-on (:transducers + :transducers/jzon + :transducers/fset + :fset + :parachute + :str) + :components ((:module "tests" + :components + ((:file "main")))) + :description "Test system for transducers" + :perform (test-op (op c) (symbol-call :parachute :test :transducers/tests))) diff --git a/vendored/transducers/transducers/deprecated.lisp b/vendored/transducers/transducers/deprecated.lisp new file mode 100644 index 0000000..738b4bc --- /dev/null +++ b/vendored/transducers/transducers/deprecated.lisp @@ -0,0 +1,20 @@ +(in-package :transducers) + +(defmacro reduced-p (item) + `(reduced? ,item)) + +(defmacro any (pred) + "Deprecated: Use `any?'." + (warn "`any' is deprecated; use `any?' instead.") + `(anyp ,pred)) + +(defmacro anyp (pred) + `(any? ,pred)) + +(defmacro all (pred) + "Deprecated: Use `all?'." + (warn "`all' is deprecated; use `all?' instead.") + `(allp ,pred)) + +(defmacro allp (pred) + `(all? ,pred)) diff --git a/vendored/transducers/transducers/entry.lisp b/vendored/transducers/transducers/entry.lisp index 041af53..da5c748 100644 --- a/vendored/transducers/transducers/entry.lisp +++ b/vendored/transducers/transducers/entry.lisp @@ -92,6 +92,7 @@ streamed as-is as cons cells." # Conditions - `no-transduce-implementation': an unsupported type was transduced over." + (declare (ignore xform f)) (error 'no-transduce-implementation :type (type-of fallback))) #+nil @@ -118,12 +119,12 @@ streamed as-is as cons cells." (declaim (ftype (function ((function (&optional t t) *) t list) *) list-reduce)) (defun list-reduce (f identity lst) - (declare (optimize (speed 3))) + (declare (optimize (speed 3) (safety 1) (debug 1))) (labels ((recurse (acc items) (if (null items) acc (let ((v (safe-call f acc (car items)))) - (if (reduced-p v) + (if (reduced? v) (reduced-val v) (recurse v (cdr items))))))) (recurse identity lst))) @@ -140,13 +141,13 @@ streamed as-is as cons cells." (funcall xf result))) (defun vector-reduce (f identity vec) - (declare (optimize (speed 3))) + (declare (optimize (speed 3) (safety 1) (debug 1))) (let ((len (length vec))) (labels ((recurse (acc i) (if (= i len) acc (let ((acc (safe-call f acc (aref vec i)))) - (if (reduced-p acc) + (if (reduced? acc) (reduced-val acc) (recurse acc (1+ i))))))) (recurse identity 0)))) @@ -161,14 +162,14 @@ streamed as-is as cons cells." (funcall xf result))) (defun reversed-reduce (f identity rev) - (declare (optimize (speed 3))) + (declare (optimize (speed 3) (safety 1) (debug 1))) (let* ((vec (reversed-vector rev)) (len (length vec))) (labels ((recurse (acc i) (if (< i 0) acc (let ((acc (safe-call f acc (aref vec i)))) - (if (reduced-p acc) + (if (reduced? acc) (reduced-val acc) (recurse acc (1- i))))))) (recurse identity (1- len))))) @@ -192,14 +193,14 @@ streamed as-is as cons cells." (funcall xf result))) (defun hash-table-reduce (f identity ht) - (declare (optimize (speed 3))) + (declare (optimize (speed 3) (safety 1) (debug 1))) (with-hash-table-iterator (iter ht) (labels ((recurse (acc) (multiple-value-bind (entry-p key value) (iter) (if (not entry-p) acc (let ((acc (safe-call f acc (cl:cons key value)))) - (if (reduced-p acc) + (if (reduced? acc) (reduced-val acc) (recurse acc))))))) (recurse identity)))) @@ -234,13 +235,13 @@ responsiblity of the caller!" (funcall xf result))) (defun stream-reduce (f identity stream) - (declare (optimize (speed 3))) + (declare (optimize (speed 3) (safety 1) (debug 1))) (labels ((recurse (acc) (let ((line (read-line stream nil))) (if (not line) acc (let ((acc (safe-call f acc line))) - (if (reduced-p acc) + (if (reduced? acc) (reduced-val acc) (recurse acc))))))) (recurse identity))) @@ -257,12 +258,12 @@ responsiblity of the caller!" (funcall xf result))) (defun generator-reduce (f identity gen) - (declare (optimize (speed 3))) + (declare (optimize (speed 3) (safety 1) (debug 1))) (labels ((recurse (acc) (let ((val (funcall (generator-func gen)))) (cond ((eq *done* val) acc) (t (let ((acc (safe-call f acc val))) - (if (reduced-p acc) + (if (reduced? acc) (reduced-val acc) (recurse acc)))))))) (recurse identity))) @@ -276,7 +277,7 @@ responsiblity of the caller!" (declaim (ftype (function ((function (&optional t t) *) t plist) *) plist-reduce)) (defun plist-reduce (f identity lst) - (declare (optimize (speed 3))) + (declare (optimize (speed 3) (safety 1) (debug 1))) (labels ((recurse (acc items) (cond ((null items) acc) ((null (cdr items)) @@ -287,7 +288,7 @@ responsiblity of the caller!" :interactive (lambda () (prompt-new-value (format nil "Value for key ~a: " key))) (recurse acc (list key value)))))) (t (let ((v (safe-call f acc (cl:cons (car items) (second items))))) - (if (reduced-p v) + (if (reduced? v) (reduced-val v) (recurse v (cdr (cdr items))))))))) (recurse identity (plist-list lst)))) diff --git a/vendored/transducers/transducers/package.lisp b/vendored/transducers/transducers/package.lisp index 1e37c4e..4d3651e 100644 --- a/vendored/transducers/transducers/package.lisp +++ b/vendored/transducers/transducers/package.lisp @@ -20,10 +20,10 @@ ;; --- Reducers -- ;; (:export #:cons #:snoc #:vector #:string #:hash-table #:count #:average #:median - #:anyp #:allp #:any #:all + #:any? #:all? #:anyp #:allp #:any #:all #:first #:last #:fold #:max #:min #:find - #:for-each) + #:for #:for-each) ;; --- Sources --- ;; (:export #:ints #:cycle #:repeat #:random #:shuffle #:plist #:reversed) @@ -35,14 +35,14 @@ #:retry-item) ;; --- Utilities --- ;; (:export #:comp #:const - #:reduced #:make-reduced #:reduced-p #:reduced-val) + #:reduced #:make-reduced #:reduced? #:reduced-p #:reduced-val) (:documentation "Ergonomic, efficient data processing.")) (in-package :transducers) ;; --- Types --- ;; -(defstruct reduced +(defstruct (reduced (:predicate reduced?)) "A wrapper that signals that reduction has completed." val) diff --git a/vendored/transducers/transducers/reducers.lisp b/vendored/transducers/transducers/reducers.lisp index 41dc0a7..2c1b182 100644 --- a/vendored/transducers/transducers/reducers.lisp +++ b/vendored/transducers/transducers/reducers.lisp @@ -1,71 +1,71 @@ (in-package :transducers) (declaim (ftype (function (&optional list t) list) cons)) -(defun cons (&optional (acc nil a-p) (input nil i-p)) +(defun cons (&optional (acc nil a?) (input nil i?)) "Reducer: Collect all results as a list." - (cond ((and a-p i-p) (cl:cons input acc)) - ((and a-p (not i-p)) (nreverse acc)) + (cond ((and a? i?) (cl:cons input acc)) + ((and a? (not i?)) (nreverse acc)) (t '()))) (declaim (ftype (function (&optional list t) list) snoc)) -(defun snoc (&optional (acc nil a-p) (input nil i-p)) +(defun snoc (&optional (acc nil a?) (input nil i?)) "Reducer: Collect all results as a list, but results are reversed. In theory, slightly more performant than `cons' since it performs no final reversal." - (cond ((and a-p i-p) (cl:cons input acc)) - ((and a-p (not i-p)) acc) + (cond ((and a? i?) (cl:cons input acc)) + ((and a? (not i?)) acc) (t '()))) -(defun string (&optional (acc nil a-p) (input #\z i-p)) +(defun string (&optional (acc nil a?) (input #\z i?)) "Reducer: Collect a stream of characters into to a single string." - (cond ((and a-p i-p) (cl:cons input acc)) - ((and a-p (not i-p)) (cl:concatenate 'cl:string (nreverse acc))) + (cond ((and a? i?) (cl:cons input acc)) + ((and a? (not i?)) (cl:concatenate 'cl:string (nreverse acc))) (t '()))) #+nil (string-transduce (map #'char-upcase) #'string "hello") -(defun vector (&optional (acc nil a-p) (input nil i-p)) +(defun vector (&optional (acc nil a?) (input nil i?)) "Reducer: Collect a stream of values into a vector." - (cond ((and a-p i-p) (cl:cons input acc)) - ((and a-p (not i-p)) (cl:concatenate 'cl:vector (nreverse acc))) + (cond ((and a? i?) (cl:cons input acc)) + ((and a? (not i?)) (cl:concatenate 'cl:vector (nreverse acc))) (t '()))) #+nil (vector-transduce (map #'1+) #'vector #(1 2 3)) (declaim (ftype (function (&optional (or cl:hash-table null) t) cl:hash-table) hash-table)) -(defun hash-table (&optional (acc nil a-p) (input nil i-p)) +(defun hash-table (&optional (acc nil a?) (input nil i?)) "Reducer: Collect a stream of key-value cons pairs into a hash table." - (cond ((and a-p i-p) (destructuring-bind (key . val) input - (setf (gethash key acc) val) - acc)) - ((and a-p (not i-p)) acc) + (cond ((and a? i?) (destructuring-bind (key . val) input + (setf (gethash key acc) val) + acc)) + ((and a? (not i?)) acc) (t (make-hash-table :test #'equal)))) #+nil (transduce #'enumerate #'hash-table '("a" "b" "c")) (declaim (ftype (function (&optional fixnum t) fixnum) count)) -(defun count (&optional (acc 0 a-p) (input nil i-p)) +(defun count (&optional (acc 0 a?) (input nil i?)) "Reducer: Count the number of elements that made it through the transduction." (declare (ignore input)) - (cond ((and a-p i-p) (1+ acc)) - ((and a-p (not i-p)) acc) + (cond ((and a? i?) (1+ acc)) + ((and a? (not i?)) acc) (t 0))) #+nil (transduce #'pass #'count '(1 2 3 4 5)) -(defun median (&optional (acc nil a-p) (input nil i-p)) +(defun median (&optional (acc nil a?) (input nil i?)) "Reducer: Calculate the median value of all numeric elements in a transduction. The elements are sorted once before the median is extracted. # Conditions - `empty-transduction': when no values made it through the transduction." - (cond ((and a-p i-p) (cl:cons input acc)) - ((and a-p (not i-p)) + (cond ((and a? i?) (cl:cons input acc)) + ((and a? (not i?)) (if (null acc) (error 'empty-transduction :msg "`median' called on an empty transduction.") ;; HACK 2024-08-22 More robust comparison. @@ -85,16 +85,16 @@ The elements are sorted once before the median is extracted. #+nil (transduce #'pass #'median '(0 1 2 3 4)) -(defun average (&optional (acc nil a-p) (input nil i-p)) +(defun average (&optional (acc nil a?) (input nil i?)) "Reducer: Calculate the average value of all numeric elements in a transduction. # Conditions - `empty-transduction': when no values made it through the transduction." - (cond ((and a-p i-p) + (cond ((and a? i?) (destructuring-bind (count . total) acc (cl:cons (1+ count) (+ total input)))) - ((and a-p (not i-p)) + ((and a? (not i?)) (destructuring-bind (count . total) acc (if (= 0 count) (error 'empty-transduction :msg "`average' called on an empty transduction.") @@ -106,52 +106,42 @@ The elements are sorted once before the median is extracted. #+nil (transduce (filter #'evenp) #'average '(1 3 5)) -(defmacro any (pred) - "Deprecated: Use `anyp'." - (warn "`any' is deprecated; use `anyp' instead.") - `(anyp ,pred)) - -(declaim (ftype (function ((function (t) *)) *) anyp)) -(defun anyp (pred) +(declaim (ftype (function ((function (t) *)) *) any?)) +(defun any? (pred) "Reducer: Yield t if any element in the transduction satisfies PRED. Short-circuits the transduction as soon as the condition is met." - (lambda (&optional (acc nil a-p) (input nil i-p)) - (cond ((and a-p i-p) + (lambda (&optional (acc nil a?) (input nil i?)) + (cond ((and a? i?) (if (funcall pred input) (reduced t) nil)) - ((and a-p (not i-p)) acc) + ((and a? (not i?)) acc) (t nil)))) #+nil -(transduce #'pass (anyp #'evenp) '(1 3 5 7 9)) +(transduce #'pass (any? #'evenp) '(1 3 5 7 9)) #+nil -(transduce #'pass (anyp #'evenp) '(1 3 5 2 7 9)) - -(defmacro all (pred) - "Deprecated: Use `allp'." - (warn "`all' is deprecated; use `allp' instead.") - `(allp ,pred)) +(transduce #'pass (any? #'evenp) '(1 3 5 2 7 9)) -(declaim (ftype (function ((function (t) *)) *) allp)) -(defun allp (pred) +(declaim (ftype (function ((function (t) *)) *) all?)) +(defun all? (pred) "Reducer: Yield t if all elements of the transduction satisfy PRED. Short-circuits with NIL if any element fails the test." - (lambda (&optional (acc nil a-p) (input nil i-p)) - (cond ((and a-p i-p) + (lambda (&optional (acc nil a?) (input nil i?)) + (cond ((and a? i?) (let ((test (funcall pred input))) (if (and acc test) t (reduced nil)))) - ((and a-p (not i-p)) acc) + ((and a? (not i?)) acc) (t t)))) #+nil -(transduce #'pass (all #'oddp) '(1 3 5 7 9)) +(transduce #'pass (all? #'oddp) '(1 3 5 7 9)) #+nil -(transduce #'pass (all #'oddp) '(1 3 5 7 9 2)) +(transduce #'pass (all? #'oddp) '(1 3 5 7 9 2)) -(defun first (&optional (acc 'transducers-none a-p) (input nil i-p)) +(defun first (&optional (acc 'transducers-none a?) (input nil i?)) "Reducer: Yield the first value of the transduction. As soon as this first value is yielded, the entire transduction stops. @@ -159,8 +149,8 @@ is yielded, the entire transduction stops. - `empty-transduction': when no values made it through the transduction. " - (cond ((and a-p i-p) (reduced input)) - ((and a-p (not i-p)) + (cond ((and a? i?) (reduced input)) + ((and a? (not i?)) (if (eq 'transducers-none acc) (restart-case (error 'empty-transduction :msg "first: the transduction was empty.") (use-value (value) @@ -175,15 +165,15 @@ is yielded, the entire transduction stops. #+nil (transduce (filter #'oddp) #'first '(2 4 6 10)) -(defun last (&optional (acc 'transducers-none a-p) (input nil i-p)) +(defun last (&optional (acc 'transducers-none a?) (input nil i?)) "Reducer: Yield the last value of the transduction. # Conditions - `empty-transduction': when no values made it through the transduction. " - (cond ((and a-p i-p) input) - ((and a-p (not i-p)) + (cond ((and a? i?) input) + ((and a? (not i?)) (if (eq 'transducers-none acc) (restart-case (error 'empty-transduction :msg "last: the transduction was empty.") (use-value (value) @@ -215,16 +205,16 @@ functions like this, `fold' is appropriate. - `empty-transduction': if no SEED is given and the transduction is empty. " (if seed-p - (lambda (&optional (acc nil a-p) (input nil i-p)) - (cond ((and a-p i-p) (funcall f acc input)) - ((and a-p (not i-p)) acc) + (lambda (&optional (acc nil a?) (input nil i?)) + (cond ((and a? i?) (funcall f acc input)) + ((and a? (not i?)) acc) (t seed))) - (lambda (&optional (acc nil a-p) (input nil i-p)) - (cond ((and a-p i-p) + (lambda (&optional (acc nil a?) (input nil i?)) + (cond ((and a? i?) (if (eq acc 'transducers-none) input (funcall f acc input))) - ((and a-p (not i-p)) + ((and a? (not i?)) (if (eq acc 'transducers-none) (restart-case (error 'empty-transduction :msg "fold was called without a seed, but the transduction was also empty.") (use-value (value) @@ -255,12 +245,12 @@ functions like this, `fold' is appropriate. (defun find (pred &key default) "Reducer: Find the first element in the transduction that satisfies a given PRED. Yields `nil' if no such element were found, unless a DEFAULT is provided." - (lambda (&optional (acc nil a-p) (input nil i-p)) - (cond ((and a-p i-p) + (lambda (&optional (acc nil a?) (input nil i?)) + (cond ((and a? i?) (if (funcall pred input) (reduced input) default)) - ((and a-p (not i-p)) acc) + ((and a? (not i?)) acc) (t default)))) #+nil @@ -269,10 +259,21 @@ Yields `nil' if no such element were found, unless a DEFAULT is provided." (transduce #'pass (find #'evenp :default 1000) '(1 3 5 9)) (defun for-each (&rest vargs) - "Reducer: Run through every item in a transduction for their side effects. -Throws away all results and yields t." + "Reducer: Deprecated. Use `for' instead." (declare (ignore vargs)) t) #+nil (transduce (map (lambda (n) (format t "~a~%" n))) #'for-each #(1 2 3 4)) + +(defun for (f) + "Reducer: Call some effectful function on every item to be reduced, and yield a +final T." + (lambda (&optional (acc nil a?) (input nil i?)) + (declare (ignore acc)) + (cond ((and a? i?) (funcall f input)) + ((and a? (not i?)) t) + (t nil)))) + +#++ +(transduce #'pass (for (lambda (n) (format t "~a~%" n))) #(1 2 3 4)) diff --git a/vendored/transducers/transducers/transducers.lisp b/vendored/transducers/transducers/transducers.lisp index 70e4865..d0cb988 100644 --- a/vendored/transducers/transducers/transducers.lisp +++ b/vendored/transducers/transducers/transducers.lisp @@ -4,16 +4,16 @@ "Transducer: Just pass along each value of the transduction. Same in intent with applying `map' to `identity', but this should be slightly more efficient. It is at least shorter to type." - (lambda (result &optional (input nil i-p)) - (if i-p (funcall reducer result input) + (lambda (result &optional (input nil i?)) + (if i? (funcall reducer result input) (funcall reducer result)))) (declaim (ftype (function ((function (t) *)) *) map)) (defun map (f) "Transducer: Apply a function F to all elements of the transduction." (lambda (reducer) - (lambda (result &optional (input nil i-p)) - (if i-p (funcall reducer result (funcall f input)) + (lambda (result &optional (input nil i?)) + (if i? (funcall reducer result (funcall f input)) (funcall reducer result))))) #+nil @@ -23,10 +23,10 @@ at least shorter to type." (defun filter (pred) "Transducer: Only keep elements from the transduction that satisfy PRED." (lambda (reducer) - (lambda (result &optional (input nil i-p)) - (if i-p (if (funcall pred input) - (funcall reducer result input) - result) + (lambda (result &optional (input nil i?)) + (if i? (if (funcall pred input) + (funcall reducer result input) + result) (funcall reducer result))))) #+nil @@ -41,11 +41,11 @@ keep results that are non-nil. => (2 5 8) " (lambda (reducer) - (lambda (result &optional (input nil i-p)) - (if i-p (let ((x (funcall f input))) - (if x - (funcall reducer result x) - result)) + (lambda (result &optional (input nil i?)) + (if i? (let ((x (funcall f input))) + (if x + (funcall reducer result x) + result)) (funcall reducer result))))) #+nil @@ -56,12 +56,11 @@ keep results that are non-nil. "Transducer: Drop the first N elements of the transduction." (lambda (reducer) (let ((new-n (1+ n))) - (lambda (result &optional (input nil i-p)) - (cond (i-p - (setf new-n (1- new-n)) - (if (> new-n 0) - result - (funcall reducer result input))) + (lambda (result &optional (input nil i?)) + (cond (i? (setf new-n (1- new-n)) + (if (> new-n 0) + result + (funcall reducer result input))) (t (funcall reducer result))))))) #+nil @@ -72,11 +71,11 @@ keep results that are non-nil. "Transducer: Drop elements from the front of the transduction that satisfy PRED." (lambda (reducer) (let ((drop? t)) - (lambda (result &optional (input nil i-p)) - (if i-p (if (and drop? (funcall pred input)) - result - (progn (setf drop? nil) - (funcall reducer result input))) + (lambda (result &optional (input nil i?)) + (if i? (if (and drop? (funcall pred input)) + result + (progn (setf drop? nil) + (funcall reducer result input))) (funcall reducer result)))))) #+nil @@ -87,14 +86,15 @@ keep results that are non-nil. "Transducer: Keep only the first N elements of the transduction." (lambda (reducer) (let ((new-n n)) - (lambda (result &optional (input nil i-p)) - (if i-p (let ((result (if (> new-n 0) - (funcall reducer result input) - result))) - (setf new-n (1- new-n)) - (if (<= new-n 0) - (ensure-reduced result) - result)) + (lambda (result &optional (input nil i?)) + (declare (type fixnum new-n)) + (if i? (let ((result (if (> new-n 0) + (funcall reducer result input) + result))) + (setf new-n (1- new-n)) + (if (<= new-n 0) + (ensure-reduced result) + result)) (funcall reducer result)))))) #+nil @@ -107,10 +107,10 @@ keep results that are non-nil. "Transducer: Keep only elements which satisfy a given PRED, and stop the transduction as soon as any element fails the test." (lambda (reducer) - (lambda (result &optional (input nil i-p)) - (if i-p (if (not (funcall pred input)) - (reduced result) - (funcall reducer result input)) + (lambda (result &optional (input nil i?)) + (if i? (if (not (funcall pred input)) + (reduced result) + (funcall reducer result input)) (funcall reducer result))))) #+nil @@ -118,11 +118,11 @@ transduction as soon as any element fails the test." (defun uncons (reducer) "Transducer: Split up a transduction of cons cells." - (lambda (result &optional (input nil i-p)) - (if i-p (let ((res (funcall reducer result (car input)))) - (if (reduced-p res) - res - (funcall reducer res (cdr input)))) + (lambda (result &optional (input nil i?)) + (if i? (let ((res (funcall reducer result (car input)))) + (if (reduced? res) + res + (funcall reducer res (cdr input)))) (funcall reducer result)))) #+nil @@ -137,11 +137,11 @@ transduction as soon as any element fails the test." (defun concatenate (reducer) "Transducer: Concatenate all the sublists and subvectors in the transduction." (let ((preserving-reducer (preserving-reduced reducer))) - (lambda (result &optional (input nil i-p)) - (if i-p (etypecase input - (cl:list (list-reduce preserving-reducer result input)) - (cl:vector (vector-reduce preserving-reducer result input)) - (t (error 'unusable-type :type (type-of input)))) + (lambda (result &optional (input nil i?)) + (if i? (etypecase input + (cl:list (list-reduce preserving-reducer result input)) + (cl:vector (vector-reduce preserving-reducer result input)) + (t (error 'unusable-type :type (type-of input)))) (funcall reducer result))))) #+nil @@ -158,11 +158,11 @@ transduction as soon as any element fails the test." (defun flatten (reducer) "Transducer: Entirely flatten all lists and vectors in the transduction, regardless of nesting." - (lambda (result &optional (input nil i-p)) - (if i-p (etypecase input - (cl:list (list-reduce (preserving-reduced (flatten reducer)) result input)) - (cl:vector (vector-reduce (preserving-reduced (flatten reducer)) result input)) - (t (funcall reducer result input))) + (lambda (result &optional (input nil i?)) + (if i? (etypecase input + (cl:list (list-reduce (preserving-reduced (flatten reducer)) result input)) + (cl:vector (vector-reduce (preserving-reduced (flatten reducer)) result input)) + (t (funcall reducer result input))) (funcall reducer result)))) #+nil @@ -186,8 +186,8 @@ any accumulated state, which may be shorter than N. (lambda (reducer) (let ((i 0) (collect '())) - (lambda (result &optional (input nil i-p)) - (cond (i-p + (lambda (result &optional (input nil i?)) + (cond (i? (setf collect (cl:cons input collect)) (setf i (1+ i)) (if (< i n) @@ -200,7 +200,7 @@ any accumulated state, which may be shorter than N. result (funcall reducer result (reverse collect))))) (setf i 0) - (if (reduced-p result) + (if (reduced? result) (funcall reducer (reduced-val result)) (funcall reducer result)))))))))) @@ -221,21 +221,21 @@ transduction. (lambda (reducer) (let ((prev 'nothing) (collect '())) - (lambda (result &optional (input nil i-p)) - (if i-p (let ((fout (funcall f input))) - (if (or (equal fout prev) (eq prev 'nothing)) - (progn (setf prev fout) - (setf collect (cl:cons input collect)) - result) - (let ((next-input (reverse collect))) - (setf prev fout) - (setf collect (list input)) - (funcall reducer result next-input)))) + (lambda (result &optional (input nil i?)) + (if i? (let ((fout (funcall f input))) + (if (or (equal fout prev) (eq prev 'nothing)) + (progn (setf prev fout) + (setf collect (cl:cons input collect)) + result) + (let ((next-input (reverse collect))) + (setf prev fout) + (setf collect (list input)) + (funcall reducer result next-input)))) (let ((result (if (null collect) result (funcall reducer result (reverse collect))))) (setf collect '()) - (if (reduced-p result) + (if (reduced? result) (funcall reducer (reduced-val result)) (funcall reducer result)))))))) @@ -246,14 +246,14 @@ transduction. "Transducer: Insert an ELEM between each value of the transduction." (lambda (reducer) (let ((send-elem? nil)) - (lambda (result &optional (input nil i-p)) - (if i-p (if send-elem? - (let ((result (funcall reducer result elem))) - (if (reduced-p result) - result + (lambda (result &optional (input nil i?)) + (if i? (if send-elem? + (let ((result (funcall reducer result elem))) + (if (reduced? result) + result + (funcall reducer result input))) + (progn (setf send-elem? t) (funcall reducer result input))) - (progn (setf send-elem? t) - (funcall reducer result input))) (funcall reducer result)))))) #+nil @@ -263,10 +263,10 @@ transduction. "Transducer: Index every value passed through the transduction into a cons pair. Starts at 0." (let ((n 0)) - (lambda (result &optional (input nil i-p)) - (if i-p (let ((input (cl:cons n input))) - (setf n (1+ n)) - (funcall reducer result input)) + (lambda (result &optional (input nil i?)) + (if i? (let ((input (cl:cons n input))) + (setf n (1+ n)) + (funcall reducer result input)) (funcall reducer result))))) #+nil @@ -277,8 +277,8 @@ Starts at 0." LOGGER must accept the running results and the current element as input. The original items of the transduction are passed through as-is." (lambda (reducer) - (lambda (result &optional (input nil i-p)) - (cond (i-p + (lambda (result &optional (input nil i?)) + (cond (i? (funcall logger result input) (funcall reducer result input)) (t (funcall reducer result)))))) @@ -305,8 +305,8 @@ input than N, then this yields nothing. (lambda (reducer) (let ((i 0) (q '())) - (lambda (result &optional (input nil i-p)) - (cond (i-p + (lambda (result &optional (input nil i?)) + (cond (i? (setf q (append q (list input))) (setf i (1+ i)) (cond ((< i n) result) @@ -339,12 +339,12 @@ Stateful; this uses a Hash Table internally so could get quite heavy if you're not careful." (lambda (reducer) (let ((seen (make-hash-table :test #'equal))) - (lambda (result &optional (input nil i-p)) - (if i-p (let ((mapped (funcall f input))) - (if (gethash mapped seen) - result - (progn (setf (gethash mapped seen) t) - (funcall reducer result input)))) + (lambda (result &optional (input nil i?)) + (if i? (let ((mapped (funcall f input))) + (if (gethash mapped seen) + result + (progn (setf (gethash mapped seen) t) + (funcall reducer result input)))) (funcall reducer result)))))) #++ @@ -355,11 +355,11 @@ not careful." (defun dedup (reducer) "Transducer: Remove adjacent duplicates from the transduction." (let ((prev 'nothing)) - (lambda (result &optional (input nil i-p)) - (if i-p (if (equal prev input) - result - (progn (setf prev input) - (funcall reducer result input))) + (lambda (result &optional (input nil i?)) + (if i? (if (equal prev input) + result + (progn (setf prev input) + (funcall reducer result input))) (funcall reducer result))))) #+nil @@ -387,12 +387,12 @@ of the transduction is always included. (step value))) (lambda (reducer) (let ((curr 1)) - (lambda (result &optional (input nil i-p)) - (if i-p (if (= 1 curr) - (progn (setf curr n) - (funcall reducer result input)) - (progn (setf curr (1- curr)) - result)) + (lambda (result &optional (input nil i?)) + (if i? (if (= 1 curr) + (progn (setf curr n) + (funcall reducer result input)) + (progn (setf curr (1- curr)) + result)) (funcall reducer result))))))) #+nil @@ -407,15 +407,15 @@ applications of a given function F. => (0 1 3 6 10)" (lambda (reducer) (let ((prev seed)) - (lambda (result &optional (input nil i-p)) - (if i-p (let* ((old prev) - (result (funcall reducer result old))) - (cond ((reduced-p result) result) - (t (let ((new (funcall f prev input))) - (setf prev new) - result)))) + (lambda (result &optional (input nil i?)) + (if i? (let* ((old prev) + (result (funcall reducer result old))) + (cond ((reduced? result) result) + (t (let ((new (funcall f prev input))) + (setf prev new) + result)))) (let ((result (funcall reducer result prev))) - (cond ((reduced-p result) (funcall reducer (reduced-val result))) + (cond ((reduced? result) (funcall reducer (reduced-val result))) (t (funcall reducer result))))))))) #+nil @@ -427,19 +427,19 @@ applications of a given function F. "Transducer: Inject some ITEM onto the front of the transduction." (lambda (reducer) (let ((unused? t)) - (lambda (result &optional (input nil i-p)) - (cond ((and i-p unused?) + (lambda (result &optional (input nil i?)) + (cond ((and i? unused?) (let ((res (funcall reducer result item))) - (if (reduced-p res) + (if (reduced? res) res (progn (setf unused? nil) (funcall reducer res input))))) - (i-p (funcall reducer result input)) + (i? (funcall reducer result input)) ;; A weird case where they specified `once', but the original ;; Source itself was empty. - ((and (not i-p) unused?) + ((and (not i?) unused?) (let ((res (funcall reducer result item))) - (if (reduced-p res) + (if (reduced? res) (funcall reducer (reduced-val res)) (funcall reducer res)))) (t (funcall reducer result))))))) @@ -468,11 +468,11 @@ further parse them yourself. This function is expected to be passed \"bare\" to `transduce', so there is no need for the caller to manually pass a REDUCER." (let ((headers nil)) - (lambda (result &optional (input nil i-p)) - (if i-p (let ((items (split-csv-line input))) - (if headers (funcall reducer result (zipmap headers items)) - (progn (setf headers items) - result))) + (lambda (result &optional (input nil i?)) + (if i? (let ((items (split-csv-line input))) + (if headers (funcall reducer result (zipmap headers items)) + (progn (setf headers items) + result))) (funcall reducer result))))) #+nil @@ -507,14 +507,14 @@ table whose keys are strings that match the values found in HEADERS. (into-csv value))) (lambda (reducer) (let ((unsent t)) - (lambda (result &optional (input nil i-p)) - (if i-p (if unsent - (let ((res (funcall reducer result (recsv headers)))) - (if (reduced-p res) - res - (progn (setf unsent nil) - (funcall reducer res (table-vals->csv headers input))))) - (funcall reducer result (table-vals->csv headers input))) + (lambda (result &optional (input nil i?)) + (if i? (if unsent + (let ((res (funcall reducer result (recsv headers)))) + (if (reduced? res) + res + (progn (setf unsent nil) + (funcall reducer res (table-vals->csv headers input))))) + (funcall reducer result (table-vals->csv headers input))) (funcall reducer result))))))) #+nil @@ -562,10 +562,10 @@ sides! (lambda (reducer) (let ((fa (funcall ta reducer)) (fb (funcall tb reducer))) - (lambda (result &optional (input nil i-p)) - (if i-p (if (funcall pred input) - (funcall fa result input) - (funcall fb result input)) + (lambda (result &optional (input nil i?)) + (if i? (if (funcall pred input) + (funcall fa result input) + (funcall fb result input)) ;; It _shouldn't_ matter that we're skipping the fork, since if ;; no input is left, we want to get access to the "real" reducer ;; at the bottom of the composed transducer stack. We know is at @@ -589,9 +589,9 @@ of the branch." (lambda (reducer) (let ((fa (funcall ta ra)) (other-res (funcall ra))) - (lambda (result &optional (input nil i-p)) - (cond (i-p - (unless (reduced-p other-res) + (lambda (result &optional (input nil i?)) + (cond (i? + (unless (reduced? other-res) (setf other-res (funcall fa other-res input))) (funcall reducer result input)) (t (cl:cons (funcall reducer result) @@ -611,14 +611,14 @@ immediately after this point. Accumulates, such that each new injection appears before the previous one." (lambda (reducer) (let ((reducer reducer)) - (lambda (result &optional (input nil i-p)) - (if i-p (let ((new-res (funcall reducer result input))) - (if (eq result new-res) - new-res - (let* ((xform (funcall f input)) - (next (funcall xform reducer))) - (setf reducer next) - new-res))) + (lambda (result &optional (input nil i?)) + (if i? (let ((new-res (funcall reducer result input))) + (if (eq result new-res) + new-res + (let* ((xform (funcall f input)) + (next (funcall xform reducer))) + (setf reducer next) + new-res))) (funcall reducer result)))))) #+nil @@ -656,14 +656,14 @@ transducer `tri' for an alternative. (lambda (reducer) (let ((fa (funcall ta #'last)) (fb (funcall tb #'last))) - (lambda (result &optional (input nil i-p)) - (if i-p (let ((ra (funcall fa result input)) - (rb (funcall fb result input))) - (cond ((reduced-p ra) ra) - ((reduced-p rb) rb) - ((eq ra result) result) - ((eq rb result) result) - (t (funcall reducer result (funcall f ra rb))))) + (lambda (result &optional (input nil i?)) + (if i? (let ((ra (funcall fa result input)) + (rb (funcall fb result input))) + (cond ((reduced? ra) ra) + ((reduced? rb) rb) + ((eq ra result) result) + ((eq rb result) result) + (t (funcall reducer result (funcall f ra rb))))) (funcall reducer result)))))) #+nil @@ -687,14 +687,14 @@ transducer `tri' for an alternative. (b-id (funcall rb)) (res-a a-id) (res-b b-id)) - (lambda (result &optional (input nil i-p)) - (cond (i-p - (unless (reduced-p res-a) + (lambda (result &optional (input nil i?)) + (cond (i? + (unless (reduced? res-a) (setf res-a (funcall fa res-a input))) - (unless (reduced-p res-b) + (unless (reduced? res-b) (setf res-b (funcall fb res-b input))) - (when (and (reduced-p res-a) - (reduced-p res-b)) + (when (and (reduced? res-a) + (reduced? res-b)) (let* ((fused (funcall f (funcall fa (reduced-val res-a)) (funcall fb (reduced-val res-b)))) diff --git a/vendored/transducers/transducers/utils.lisp b/vendored/transducers/transducers/utils.lisp index 8a68006..7eda2d1 100644 --- a/vendored/transducers/transducers/utils.lisp +++ b/vendored/transducers/transducers/utils.lisp @@ -30,14 +30,14 @@ (declaim (ftype (function ((or t reduced)) reduced) ensure-reduced)) (defun ensure-reduced (x) "Ensure that X is reduced." - (if (reduced-p x) + (if (reduced? x) x (reduced x))) (declaim (ftype (function ((or t reduced)) *) ensure-unreduced)) (defun ensure-unreduced (x) "Ensure that X is unreduced." - (if (reduced-p x) + (if (reduced? x) (reduced-val x) x)) @@ -49,7 +49,7 @@ early and returns a reduced value, list-reduce would 'unreduce' that value and try to continue the transducing process." (lambda (a b) (let ((result (funcall reducer a b))) - (if (reduced-p result) + (if (reduced? result) (reduced result) result)))) @@ -82,15 +82,17 @@ starting the separation from the end, e.g. when called with arguments (incf words) (setf end start)))))) +(declaim (ftype (function (cl:string &key (:separator character)) list) string-split)) (defun string-split (string &key (separator #\space)) "You know what this does." (labels ((recurse (acc start end) + (declare (type fixnum start end)) (cond ((and (<= start 0) (<= end 0)) acc) ;; FIXME: 2025-01-13 This case can probably be simplified. - ((and (zerop start) (eql separator (aref string start))) + ((and (zerop start) (eql separator (char string start))) (cl:cons "" (cl:cons (subseq string (1+ start) (1+ end)) acc))) ((zerop start) (cl:cons (subseq string start (1+ end)) acc)) - ((eql separator (aref string start)) + ((eql separator (char string start)) (recurse (cl:cons (subseq string (1+ start) (1+ end)) acc) (1- start) (1- start))) @@ -100,5 +102,7 @@ starting the separation from the end, e.g. when called with arguments #++ (subseq "hello" 0 2) + #++ (string-split ",Hello,my,name,is,Colin," :separator #\,) +