I'm not sure which inputs cause your function to explode. I can't get him to blow up two 5 billionth subsequences:
boot.user=> (defn move-split [[xs ys]] #_=> (doall #_=> (concat #_=> (list (concat xs (list (first ys)))) #_=> (list (next ys))))) #'boot.user/move-split boot.user=> (def x (move-split [(range 5e0) (range 5e0)])) #'boot.user/x boot.user=> x ((0 1 2 3 4 0) (1 2 3 4)) boot.user=> (def x (move-split [(range 5e9) (range 5e9)])) #'boot.user/x boot.user=> (-> (nth x 0) first) 0 boot.user=> (-> (nth x 1) first) 1
However, there are many possibilities to make this function more idiomatic. Looking at the function:
(defn move-split [[xs ys]] (doall (concat (list (concat xs (list (first ys)))) (list (next ys)))))
On line 4: we donβt need to build a list , since concat already returns the sequence. Similarly, we will not build list on line 5, or next already return the sequence. With these two changes:
(defn move-split [[xs ys]] (doall (concat (concat xs (list (first ys))) (next ys))))
Now, why in the world would we doall up a perfectly good (lazy) function by pasting a doall at the top? Let's get rid of this. Also, since the function should return a two-element sequence, why not just do it instead of calling concat on line 3? The idiomatic way of constructing a two-element sequence is through the syntax of a vector literal. With these two changes we have:
(defn move-split [[xs ys]] [(concat xs (list (first ys)))(next ys)])
Oh, and we can again apply the vector literal syntax instead of this list constructor, providing us with this final version:
(defn move-split [[xs ys]] [(concat xs [(first ys)])(next ys)])
Now try this fn out ...
boot.user=> (defn move-split [[xs ys]] #_=> [(concat xs [(first ys)])(rest ys)]) #'boot.user/move-split boot.user=> [(range 5e0) (range 5e0)] [(0 1 2 3 4) (0 1 2 3 4)] boot.user=> (def x (move-split [(range 5e0) (range 5e0)])) #'boot.user/x boot.user=> x [(0 1 2 3 4 0) (1 2 3 4)] boot.user=> (-> (nth x 0) first) 0 boot.user=> (-> (nth x 1) first) 1
It seems to work. What if we have two huge subsequences? Let's try this with a pair of 5 billion subsequences:
boot.user=> (def x (move-split [(range 5e9) (range 5e9)]))
In your initial question, as I said, I donβt know what source data called the original function in order to explode the stack. I saw this in the concat doc documentation:
Concatenated concatenations do not smooth out automatically! Thus, clj :: clojure.core / reduce with concat generates very nested structures and can easily generate stack overflows when trying to execute the resulting sequence. (apply concat ...).
Let us use a reduce to concat sequence of 10 sequences of 10 integers:
boot.user=> (take 5 (reduce concat [] (for [x (range 1e1)] (range 1e1)))) (0 1 2 3 4)
But if we try to use reduce to concat sequence of 10,000 sequences of 10 integers:
boot.user=> (take 5 (reduce concat [] (for [x (range 1e5)] (range 1e1)))) java.lang.StackOverflowError:
Using apply instead works fine:
boot.user=> (take 5 (apply concat (for [x (range 1e5)] (range 1e1)))) (0 1 2 3 4)
There is just something to keep in mind when concat .