2015-07-19 77 views
3

我想要生成一個相對較小的(1296元素)列表的基本上枚舉4個基地6位數從[0 0 0 0]到[5 5 5 5]爲什麼我得到一個函數StackoverflowError沒有明確的遞歸

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

目前我有什麼是:

(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])))) 

這工作,但它最終吹堆棧。

我在這裏做什麼導致StackOverflowError? 我怎樣才能構建我的代碼,使其「安全」? 有沒有更好的方式來做我想做的事情?

+0

其他兩個類似的問題:http://stackoverflow.com/questions/2946764/recursive-function-causing-a-stack-overflow http://stackoverflow.com/questions/24958907/why-does-reduce-give -a-stackoverflowerror-in-clojure –

回答

4

這是由於在迭代函數體lazyiness。請注意,next-v的第一次調用返回的結果在被評估之前再次傳遞給next-v(因爲它是一個惰性seq),然後next-v再次返回一個未評估的lazy-seq,它將再次傳遞給它。

當你意識到最後的懶惰seq,產生第一個元素,所有的鏈接seqs必須實現,以通過你的初始[0 0 0 0]。這會打擊堆棧。

斯圖爾特塞拉利昂寫了這樣的好文章舉出更多的例子:http://stuartsierra.com/2015/04/26/clojure-donts-concat

你可以簡單地包裹在咱們身上的地圖索引中的呼叫vec

但是,爲您的問題找到一個更通用的算法是推薦的。

5

我會解決這個問題的方法是:

(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] 

廣義:

(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)) 
+0

謝謝。不知道爲什麼我沒有看到。 –

+0

那麼矢量的長度是可變的更一般的解決方案呢? –

+0

@GarrettRowe查看編輯答案。 –

相關問題