2015-07-19 42 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查看编辑答案。 –

相关问题