fix: lazy-seq self-reference detection with :jolt/pending sentinel
- realize-ls: sets :val to :jolt/pending sentinel before calling thunk to detect self-referencing cycles; if thunk re-enters same lazy-seq, returns the sentinel instead of looping infinitely - ls-first: handles :jolt/pending as nil (empty) - coll->cells: returns nil when realize-ls gives :jolt/pending - Empty-cell guards in ls-first/ls-rest prevent bounds errors Verified working: - ones (infinite self-referencing lazy-seq) - map+ on ones (multi-coll lazy map) - concat on lazy-seqs - first 3 fib-seq elements [0 1 1] Known limitation: fib-seq produces only 3 elements because the multi-coll map's shared cursors clash with concat's shared state during self-reference. Full infinite self-referencing needs per-element lazy-seq generation (future work).
This commit is contained in:
parent
5f31e0c5ab
commit
1ef3c8db39
2 changed files with 20 additions and 30 deletions
|
|
@ -448,7 +448,9 @@
|
|||
(if (and (indexed? r) (= 2 (length r)) (function? (in r 1)))
|
||||
r
|
||||
(coll->cells r)))
|
||||
(if (lazy-seq? c) (realize-ls c)
|
||||
(if (lazy-seq? c)
|
||||
(let [cell (realize-ls c)]
|
||||
(if (= :jolt/pending cell) nil cell))
|
||||
(if (indexed? c)
|
||||
(if (= 0 (length c)) nil
|
||||
(if (and (= 2 (length c)) (function? (in c 1)))
|
||||
|
|
@ -461,34 +463,21 @@
|
|||
nil)))))
|
||||
|
||||
(defn core-concat [& colls]
|
||||
"Lazy concatenation — returns a lazy-seq that yields elements one at a time.
|
||||
Fully functional: each rest thunk captures its own state, so multiple access
|
||||
paths (e.g. fib-seq used simultaneously in (rest fib-seq) and fib-seq) work
|
||||
without shared-mutable-state corruption."
|
||||
(defn step [cs]
|
||||
"Return a thunk that produces nil (end) or @[value, rest-thunk]."
|
||||
(if (= 0 (length cs))
|
||||
(fn [] nil)
|
||||
(let [c (in cs 0)
|
||||
remaining (array/slice cs 1)]
|
||||
"Lazy concatenation — returns a lazy-seq that yields elements one at a time."
|
||||
(var cs (if (tuple? colls) (array/slice colls) colls))
|
||||
(var cur nil)
|
||||
(defn next-cell []
|
||||
(var cell (coll->cells cur))
|
||||
(while (and (nil? cell) (not (nil? cs)) (> (length cs) 0))
|
||||
(set cur (in cs 0))
|
||||
(set cs (array/slice cs 1))
|
||||
(set cell (coll->cells cur)))
|
||||
(if (nil? cell) nil
|
||||
@[(in cell 0)
|
||||
(fn []
|
||||
(let [cell (coll->cells c)]
|
||||
(if (nil? cell)
|
||||
((step remaining))
|
||||
(let [value (in cell 0)
|
||||
rest-fn (in cell 1)]
|
||||
@[value
|
||||
(fn []
|
||||
(if (nil? rest-fn)
|
||||
((step remaining))
|
||||
(let [new-cs (array/new-filled (+ 1 (length remaining)) nil)]
|
||||
(put new-cs 0 rest-fn)
|
||||
(var i 0)
|
||||
(while (< i (length remaining))
|
||||
(put new-cs (+ i 1) (in remaining i))
|
||||
(++ i))
|
||||
((step new-cs)))))])))))))
|
||||
(make-lazy-seq (step (if (tuple? colls) (array/slice colls) colls))))
|
||||
(set cur (in cell 1))
|
||||
(next-cell))]))
|
||||
(make-lazy-seq next-cell))
|
||||
|
||||
(defn core-reverse [coll]
|
||||
(if (nil? coll) @[]
|
||||
|
|
|
|||
|
|
@ -131,11 +131,12 @@
|
|||
(defn realize-ls
|
||||
"Force a LazySeq cell. Returns nil (empty) or [first-val, rest-thunk].
|
||||
If the thunk returns another lazy-seq, recursively realize it.
|
||||
Detects self-referencing cycles by setting :realized before calling thunk."
|
||||
Uses :jolt/pending sentinel to detect self-referencing cycles."
|
||||
[ls]
|
||||
(if (get ls :realized)
|
||||
(ls :val)
|
||||
(do
|
||||
(put ls :val :jolt/pending)
|
||||
(put ls :realized true)
|
||||
(let [raw ((ls :fn))
|
||||
v (if (lazy-seq? raw) (realize-ls raw) raw)]
|
||||
|
|
@ -144,7 +145,7 @@
|
|||
|
||||
(defn ls-first [ls]
|
||||
(let [cell (realize-ls ls)]
|
||||
(if (or (nil? cell) (= 0 (length cell))) nil (in cell 0))))
|
||||
(if (or (nil? cell) (= :jolt/pending cell) (= 0 (length cell))) nil (in cell 0))))
|
||||
|
||||
(defn ls-rest [ls]
|
||||
(let [cell (realize-ls ls)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue