我正在尝试生成一个相对较小的(1296个元素)向量列表,基本上枚举从[0 0 0 0]到[5 5 5 5]的4个基数6位数
[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 :(得分: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))
答案 1 :(得分:4)
这是由于迭代函数体中的懒惰。请注意,第一次调用next-v返回的结果在被评估之前再次传递给next-v(因为它是一个懒惰的seq),然后next-v再次返回一个未经评估的lazy-seq,它将再次传递给它
当你意识到最终的延迟seq时,为了产生第一个元素,必须实现所有链接的seqs以通过你的初始[0 0 0 0]。这会炸掉堆栈。
Stuart Sierra通过更多例子写了一篇很好的文章:http://stuartsierra.com/2015/04/26/clojure-donts-concat
您可以简单地将地图索引的调用包装在vec
。
建议您为您的问题寻找更通用的算法。