Why am I getting a StackoverflowError for a function without explicit recursion

I am trying to create a relatively small (1296 elements) list of vectors essentially listing 4 base 6 digits from [0 0 0 0] to [5 5 5 5]

[0 0 0 0], [1 0 0 0] ... [5 0 0 0], [0 1 0 0] ... [5 5 5 5]

      

I currently have:

(letfn [(next-v [v]
          (let [active-index (some (fn [[i e]] (when (> 5 e) i)) 
                                   (map-indexed vector v))]
            (map-indexed #(cond
                           (> active-index %1) 0
                           (= active-index %1) (inc %2)
                           :else %2)
                         v)))]
  (last (take 1290 (iterate next-v [0 0 0 0]))))

      

It works but ends up hitting the stack.

What am I doing here that is causing the StackOverflowError? How can I structure my code to be "safe"? Is there a better way to do what I am trying to do?

+3


source to share


2 answers


This is due to the laziness in the tag with the function iteration. Note that the result returned by the first call to next-v is passed to next-v again before being evaluated (because its a lazy seq), then next-v returns the invaluable lazy seq again, which will be passed to it again.

When you understand the final lazy seq to create the first element, all chains must be implemented to go to your initial [0 0 0 0]. This will remove the stack.

Stuart Sierra wrote a nice article on this with lots of examples: http://stuartsierra.com/2015/04/26/clojure-donts-concat



You can simply wrap the map-converted call in the body of the let in vec

.

It is recommended to use a more general algorithm for your problem.

+4


source


How can I solve this:

(def my-range
  (for [i (range 0 6)
        j (range 0 6)
        x (range 0 6)
        y (range 0 6)]
    [i j x y]))

(nth my-range 1295) ;;=> [5 5 5 5]

      



Generalized:

(defn combine [coll]
  (for [i (range 6)
        j coll]
    (conj j i)))

(combine (map list (range 6)))
(combine (combine (map list (range 6))))
(combine (combine (combine (map list (range 6)))))

(def result (nth (iterate combine (map list (range 6))) 3))

      

+5


source







All Articles