如何使用非尾递归在clojure中生成一个惰性序列?

时间:2014-11-06 00:28:59

标签: recursion clojure lazy-sequences

以整数分区问题为例,我可以编写以下代码来输出正整数n的所有分区:

(defn foo
  ([n] (foo n n []))
  ([n k buf]
    (if (zero? n)
      (println buf)
      (doseq [i (range 1 (inc (min n k)))]
        (foo (- n i) i (conj buf i))))))

然后(foo 5)输出:

[1 1 1 1 1]
[2 1 1 1]
[2 2 1]
[3 1 1]
[3 2]
[4 1]
[5]

问题是如何编写一个函数bar来生成包含此类结果的lazy-seq?例如,我希望(bar 5)生成([1 1 1 1 1] [2 1 1 1] [2 2 1] [3 1 1] [3 2] [4 1] [5])

2 个答案:

答案 0 :(得分:2)

这是一个大致等效的函数,将分区生成为一系列序列:

(defn bar
  ([n] (bar n n))
  ([n k]
   (if (zero? n)
     [[]]
     (for [i (range 1 (inc (min n k))), tail (bar (- n i) i)]
       (cons i tail)))))

例如,

(bar 5)
; ((1 1 1 1 1) (2 1 1 1) (2 2 1) (3 1 1) (3 2) (4 1) (5))

bar有多懒?

  • for很懒。
  • 让它变得更加懒惰,将身体裹在lazy-seq中。

我怀疑上述情况。

  • 该函数递归到深度n
  • 将身体包裹在lazy-seq我怀疑只是懒得堆积 序列,在访问时产生与递归调用栈一样深的序列 第一要素。

此外,重复计算相同的tail s;因为(bar n k)对所有k >= n都是一样的,所以更是如此。

如果此功能的性能是特定问题,则存在每步的恒定时间的迭代算法。正如@ CharlesDuffy的评论暗示的那样,可以重新设置这些以产生延迟序列。


为什么当你能看书时凝视水晶球?

标准名称空间clojure.math.combinatorics,托管here,包含一个partition函数,可以生成任何对象序列的分区的延迟序列 - 快速。整数分区是我们计算相同对象的每个分区的元素的地方。它们以反向词典顺序出现。

例如

(map #(map count %) (combo/partitions (repeat 5 :whatever)))
;((5) (4 1) (3 2) (3 1 1) (2 2 1) (2 1 1 1) (1 1 1 1 1))

毫无疑问,可以删除代码来处理这种情况。

答案 1 :(得分:1)

这是递归解决方案。有一些方法可以优化它,而代码并不是最好的。

(defn partitions [n]
  (loop [m n
         res [(init-step n m)]]
    (let [l (last res)]
      (if (= m 1)
        res
        (if (last-step? (last res))
          (recur (- m 1) (vec (conj res (init-step n (- m 1)))))
          (recur m (next-step res)))))))


(defn init-step [n m]
  (if (= n m)
    [n]
    (loop [res [m (- n m)]]
      (let [l (last res)
            f (first res)]
        (if (<= l f)
          res
          (recur (vec (conj (vec (butlast res)) f (- l f)))))))))

(defn next-step [res]
  (let [input-vec (last res)
        cnt (count input-vec)
        i (.indexOf input-vec 1)
        j (if (> i -1) (- i 1) (- cnt 1))
        m (- cnt j)
        new-vec (conj (vec (take j input-vec)) (- (input-vec j) 1))]
    (conj res (vec (concat new-vec (repeat m 1))))))


(defn last-step? [input-vec]
  (if
    (or (nil? input-vec)
        (= (count input-vec) 1)
        (= (input-vec 1) 1)) true
    false))

(partitions 10)
#=> [[10] [9 1] [8 2] [8 1 1] [7 3] [7 2 1] [7 1 1 1] [6 4] [6 3 1] [6 2 1 1] [6 1 1 1 1] [5 5] [5 4 1] [5 3 1 1] [5 2 1 1 1] [5 1 1 1 1 1] [4 4 2] [4 4 1 1] [4 3 1 1 1] [4 2 1 1 1 1] [4 1 1 1 1 1 1] [3 3 3 1] [3 3 2 1 1] [3 3 1 1 1 1] [3 2 1 1 1 1 1] [3 1 1 1 1 1 1 1] [2 2 2 2 2] [2 2 2 2 1 1] [2 2 2 1 1 1 1] [2 2 1 1 1 1 1 1] [2 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1]]