Ответ 1
Используйте apply
. Когда аргумент функции ленив, то есть apply
.
Позвольте проверить, что с побочным эффектом подсчета на нижележащие подпоследовательности:
(def counter (atom 0))
(def ss (repeatedly 3000
(fn [] (repeatedly 3000
(fn [] (do (swap! counter inc) true))))))
(def foo (apply concat ss))
so.core=> @counter
0
so.core=> (dorun (take 1 foo))
nil
so.core=> @counter
1
so.core=> (dorun (take 3001 foo))
nil
so.core=> @counter
3001
reduce
с большим количеством переполнений concat
из-за состава thunk
Ленивые последовательности, такие как созданные concat
, реализуются с помощью thunks, отложенных вызовов функций. Когда вы concat
результат concat
, вы вложили thunk в другой thunk. В вашей функции вложенность занимает 3000 глубин, и, таким образом, стек переполняется, как только запрашивается первый элемент, а 3000 вложенных громкостей разматываются.
so.core=> (def bar (reduce concat (repeat 3000 (repeat 3000 true))))
#'so.core/bar
so.core=> (first bar)
StackOverflowError clojure.lang.LazySeq.seq (LazySeq.java:49)
реализация ленивых последовательностей в общем случае развяжет вложенные трюки батут-стиля, когда seq
ed и не ударит стек:
so.core=> (loop [lz [1], n 0]
(if (< n 3000) (recur (lazy-seq lz) (inc n)) lz))
(1)
Однако, если вы вызываете seq
в ленивой последовательности на нереализованной части, реализуя ее...
so.core=> (loop [lz [1], n 0]
(if (< n 3000) (recur (lazy-seq (seq lz)) (inc n)) lz))
StackOverflowError so.core/eval1405/fn--1406 (form-init584039696026177116.clj:1)
so.core=> (pst 3000)
StackOverflowError so.core/eval1619/fn--1620 (form-init584039696026177116.clj:2) clojure.lang.LazySeq.sval (LazySeq.java:40) clojure.lang.LazySeq.seq (LazySeq.java:49) clojure.lang.RT.seq (RT.java:484) clojure.core/seq (core.clj:133) so.core/eval1619/fn--1620 (form-init584039696026177116.clj:2) clojure.lang.LazySeq.sval (LazySeq.java:40) clojure.lang.LazySeq.seq (LazySeq.java:49) clojure.lang.RT.seq (RT.java:484) clojure.core/seq (core.clj:133) ... (repeatedly)
Затем вы завершаете создание фреймов стека seq
. Реализация concat
такова. Изучите трассировку стека для вашего StackOverflowError с помощью concat
, и вы увидите подобное.