diff --git a/jolt-core/clojure/core/20-coll.clj b/jolt-core/clojure/core/20-coll.clj index 5fdd597..e6e127a 100644 --- a/jolt-core/clojure/core/20-coll.clj +++ b/jolt-core/clojure/core/20-coll.clj @@ -151,36 +151,34 @@ (defn comparator [pred] (fn [a b] (cond (pred a b) -1 (pred b a) 1 :else 0))) -;; Eager (Jolt has no laziness yet): a vector of the running accumulators. +;; Lazy: the running accumulators, one at a time (matches Clojure). Recursion is +;; letfn-bound (NOT top-level self-call) so the lazy-seq body compiles cleanly in +;; the overlay — see jolt-r81. (defn reductions ([f coll] - (let [s (seq coll)] - (if s - (reductions f (first s) (rest s)) - (list (f))))) + (lazy-seq + (let [s (seq coll)] + (if s + (reductions f (first s) (rest s)) + (list (f)))))) ([f init coll] - (loop [acc init xs (seq coll) out [init]] - (if xs - (let [a (f acc (first xs))] (recur a (next xs) (conj out a))) - out)))) + (letfn [(step [acc s] + (cons acc + (lazy-seq + (let [s (seq s)] + (when s + (step (f acc (first s)) (rest s)))))))] + (step init coll)))) -;; The lazy tree-seq (using lazy-seq/make-lazy-seq) correctly implements -;; Clojure semantics but triggers compile-mode issues in self-hosted compilation. -;; When compile mode is fixed, replace the eager version below with: -;; (defn tree-seq [branch? children root] -;; (let [walk (fn walk [node] -;; (lazy-seq -;; (cons node -;; (when (branch? node) -;; (mapcat walk (children node))))))] -;; (walk root))) +;; Lazy pre-order DFS (matches Clojure). letfn-bound walk (not (fn walk …)) so it +;; compiles cleanly in the overlay under :compile? — see jolt-r81. (defn tree-seq [branch? children root] - (let [walk (fn walk [acc node] - (let [acc (conj acc node)] - (if (branch? node) - (reduce walk acc (children node)) - acc)))] - (walk [] root))) + (letfn [(walk [node] + (lazy-seq + (cons node + (when (branch? node) + (mapcat walk (children node))))))] + (walk root))) ;; Canonical flatten via tree-seq: the leaves (non-sequential nodes) in order. ;; Flattens lists too (sequential?), matching Clojure/CLJS. @@ -191,19 +189,29 @@ (defn xml-seq [root] (tree-seq (complement string?) (comp seq :content) root)) -;; Eager interleave: round-robin one element from each coll until any exhausts. -;; A lazy version (canonical Clojure cons-recursion) hits the same compile-mode -;; overlay bug as reductions/tree-seq — a self-recursive lazy-seq leaks its macro -;; expansion under :compile? (see jolt-r81). Eager until that's fixed. -(defn interleave [& colls] - (if (empty? colls) - (list) - (let [cs (mapv vec colls) - n (apply min (map count cs))] - (loop [i 0 out []] - (if (< i n) - (recur (inc i) (reduce (fn [o c] (conj o (nth c i))) out cs)) - out))))) +;; Lazy interleave: round-robin one element from each coll until any exhausts. +;; letfn-bound recursion (not top-level self-call) so the lazy-seq body compiles +;; cleanly in the overlay — see jolt-r81. +(defn interleave + ([] ()) + ([c1] (lazy-seq c1)) + ([c1 c2] + (letfn [(step [s1 s2] + (lazy-seq + (let [s1 (seq s1) s2 (seq s2)] + (when (and s1 s2) + (cons (first s1) + (cons (first s2) + (step (rest s1) (rest s2))))))))] + (step c1 c2))) + ([c1 c2 & cs] + (letfn [(step [ss] + (lazy-seq + (let [ss (map seq ss)] + (when (every? identity ss) + (concat (map first ss) + (step (map rest ss)))))))] + (step (list* c1 c2 cs))))) ;; No ratio type on Jolt, so rationalize is identity. (defn rationalize [x] x) diff --git a/test/integration/lazy-infinite-test.janet b/test/integration/lazy-infinite-test.janet index 0e14496..4d94b30 100644 --- a/test/integration/lazy-infinite-test.janet +++ b/test/integration/lazy-infinite-test.janet @@ -47,8 +47,9 @@ ["first filter even? drop range" "4" "(first (filter even? (drop 3 (range))))"] ["take 3 remove odd? range" "(quote (0 2 4))" "(take 3 (remove odd? (range)))"] ["take 3 drop-while <5 range" "(quote (5 6 7))" "(take 3 (drop-while (fn [x] (< x 5)) (range)))"] - # interleave stays eager in overlay (lazy-seq macro breaks compile mode). - # ["take 4 interleave range iterate" "(quote (0 10 1 11))" "(take 4 (interleave (range) (iterate inc 10)))"] + ["take 4 interleave range iterate" "(quote (0 10 1 11))" "(take 4 (interleave (range) (iterate inc 10)))"] + ["take 4 reductions + range" "(quote (0 1 3 6))" "(take 4 (reductions + (range)))"] + ["take 3 tree-seq infinite" "(quote (0 0 0))" "(take 3 (tree-seq (fn [_] true) (fn [n] [n]) 0))"] ["take 3 partition 2 range" "(quote ((0 1) (2 3) (4 5)))" "(take 3 (partition 2 (range)))"] ["take 3 partition-all 2 range" "(quote ((0 1) (2 3) (4 5)))" "(take 3 (partition-all 2 (range)))"] ["take 3 map-indexed vector range" "(quote ([0 0] [1 1] [2 2]))" "(take 3 (map-indexed vector (range)))"]