fix: lazy-seq cell recognition, multi-coll map laziness, nested lazy-seq realization
Three bugs discovered while testing Clojure lazy-seq patterns (fib-seq, ones): 1. coll->cells did not recognize cons cells ([$val, fn]) as already-complete cells. It treated them as regular indexed collections and re-wrapped the rest function, causing double-wrapping that returned functions instead of values. Fix: check (and (= 2 (length c)) (function? (in c 1))) before splitting an indexed collection. 2. realize-ls cached thunk results directly without checking if the result was itself a lazy-seq. When a rest thunk returned a lazy-seq table, ls-first would then call (in table 0) which returned nil or wrong values. Fix: recursively realize-ls when the thunk returns a lazy-seq. 3. core-map multi-collection path built all results eagerly in a while loop, hanging on infinite sequences like (map + ones ones). Fix: return a lazy-seq via make-lazy-seq/build-cell, interleaving one element at a time from each collection.
This commit is contained in:
parent
e2e7fa1447
commit
ec70076e92
2 changed files with 66 additions and 34 deletions
|
|
@ -303,34 +303,62 @@
|
||||||
c))
|
c))
|
||||||
|
|
||||||
(defn core-map [f & colls]
|
(defn core-map [f & colls]
|
||||||
(let [realized (map realize-for-iteration colls)
|
(if (= 1 (length colls))
|
||||||
first-coll (realized 0)
|
# Single-collection: eagerly realized (common case, fine for most uses)
|
||||||
result (if (= 1 (length colls))
|
(let [c (realize-for-iteration (colls 0))
|
||||||
(do
|
result (do
|
||||||
(var res @[])
|
(var res @[])
|
||||||
(each x first-coll (array/push res (f x)))
|
(each x c (array/push res (f x)))
|
||||||
res)
|
res)]
|
||||||
(do
|
(if (tuple? c) (tuple/slice (tuple ;result)) result))
|
||||||
(var res @[])
|
# Multi-collection: return a lazy-seq
|
||||||
(var idxs @{})
|
(let [n (length colls)
|
||||||
(each _ first-coll (array/push idxs 0))
|
cursors (array/new-filled n nil)
|
||||||
(var done false)
|
idxs (array/new-filled n 0)
|
||||||
(while (not done)
|
realized (array/new-filled n nil)]
|
||||||
(var args @[])
|
# Initialize: detect lazy-seqs vs realized collections
|
||||||
(var i 0)
|
(var i 0)
|
||||||
(while (< i (length colls))
|
(while (< i n)
|
||||||
(let [c (realized i) j (idxs i)]
|
(let [coll (colls i)]
|
||||||
(if (>= j (length c))
|
(if (lazy-seq? coll)
|
||||||
(do (set done true) (break))
|
(put cursors i coll)
|
||||||
(array/push args (c j))))
|
(do
|
||||||
(++ i))
|
(put realized i coll)
|
||||||
(if (not done) (array/push res (apply f args)))
|
(put cursors i nil))))
|
||||||
(var k 0)
|
(++ i))
|
||||||
(while (< k (length colls))
|
# Helper: get next element from collection i, advancing state.
|
||||||
(set (idxs k) (+ (idxs k) 1))
|
# Returns the element or nil if exhausted.
|
||||||
(++ k)))
|
(defn next-elem [i]
|
||||||
res))]
|
(let [cur (cursors i)
|
||||||
(if (tuple? first-coll) (tuple/slice (tuple ;result)) result)))
|
ridx (idxs i)]
|
||||||
|
(if (not (nil? cur))
|
||||||
|
(let [val (ls-first cur)]
|
||||||
|
(if (nil? val) nil
|
||||||
|
(do
|
||||||
|
(put cursors i (ls-rest cur))
|
||||||
|
(put idxs i (+ ridx 1))
|
||||||
|
val)))
|
||||||
|
(let [coll (realized i)]
|
||||||
|
(if (nil? coll)
|
||||||
|
(let [rc (realize-for-iteration (colls i))]
|
||||||
|
(put realized i rc)
|
||||||
|
(if (>= ridx (length rc)) nil
|
||||||
|
(do (put idxs i (+ ridx 1)) (rc ridx))))
|
||||||
|
(if (>= ridx (length coll)) nil
|
||||||
|
(do (put idxs i (+ ridx 1)) (coll ridx))))))))
|
||||||
|
# Build a lazy-seq of mapped results
|
||||||
|
(defn build-cell []
|
||||||
|
(var args @[])
|
||||||
|
(var i 0)
|
||||||
|
(while (< i n)
|
||||||
|
(let [v (next-elem i)]
|
||||||
|
(if (nil? v) (break nil))
|
||||||
|
(array/push args v))
|
||||||
|
(++ i))
|
||||||
|
(if (= (length args) n)
|
||||||
|
@[(apply f args) (fn [] (build-cell))]
|
||||||
|
nil))
|
||||||
|
(make-lazy-seq build-cell))))
|
||||||
|
|
||||||
(defn core-filter [pred coll]
|
(defn core-filter [pred coll]
|
||||||
(var result @[])
|
(var result @[])
|
||||||
|
|
@ -423,11 +451,13 @@
|
||||||
(if (lazy-seq? c) (realize-ls c)
|
(if (lazy-seq? c) (realize-ls c)
|
||||||
(if (indexed? c)
|
(if (indexed? c)
|
||||||
(if (= 0 (length c)) nil
|
(if (= 0 (length c)) nil
|
||||||
(let [f (in c 0)
|
(if (and (= 2 (length c)) (function? (in c 1)))
|
||||||
rest (if (> (length c) 1)
|
c # already a cell [val, rest-thunk]
|
||||||
(if (tuple? c) (tuple/slice c 1) (array/slice c 1))
|
(let [f (in c 0)
|
||||||
nil)]
|
rest (if (> (length c) 1)
|
||||||
@[f (fn [] (coll->cells rest))]))
|
(if (tuple? c) (tuple/slice c 1) (array/slice c 1))
|
||||||
|
nil)]
|
||||||
|
@[f (fn [] (coll->cells rest))])))
|
||||||
nil)))))
|
nil)))))
|
||||||
|
|
||||||
(defn core-concat [& colls]
|
(defn core-concat [& colls]
|
||||||
|
|
|
||||||
|
|
@ -129,11 +129,13 @@
|
||||||
@{:jolt/type :jolt/lazy-seq :fn thunk :realized false :val nil})
|
@{:jolt/type :jolt/lazy-seq :fn thunk :realized false :val nil})
|
||||||
|
|
||||||
(defn realize-ls
|
(defn realize-ls
|
||||||
"Force a LazySeq cell. Returns nil (empty) or [first-val, rest-thunk]."
|
"Force a LazySeq cell. Returns nil (empty) or [first-val, rest-thunk].
|
||||||
|
If the thunk returns another lazy-seq, recursively realize it."
|
||||||
[ls]
|
[ls]
|
||||||
(if (get ls :realized)
|
(if (get ls :realized)
|
||||||
(ls :val)
|
(ls :val)
|
||||||
(let [v ((ls :fn))]
|
(let [raw ((ls :fn))
|
||||||
|
v (if (lazy-seq? raw) (realize-ls raw) raw)]
|
||||||
(put ls :realized true)
|
(put ls :realized true)
|
||||||
(put ls :val v)
|
(put ls :val v)
|
||||||
v)))
|
v)))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue