【问题标题】:How to generate a lazy sequence in clojure using non-tail recursion?如何使用非尾递归在 clojure 中生成惰性序列?
【发布时间】:2014-11-06 00:28:58
【问题描述】:

以整数分区问题为例,我可以编写如下代码输出正整数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 来生成包含此类结果的惰性序列?比如我想(bar 5)生成([1 1 1 1 1] [2 1 1 1] [2 2 1] [3 1 1] [3 2] [4 1] [5])

【问题讨论】:

  • 确实,如果您想一想惰性序列是如何工作的,您会发现能够将所有状态封装为 recur 调用与拥有为延迟(延迟)执行而封装的状态!
  • @CharlesDuffy 如果你给我一个这个问题的例子,我将不胜感激。

标签: recursion clojure lazy-sequences


【解决方案1】:

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

(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 包裹身体我怀疑只是懒惰 序列,在访问 第一个元素。

此外,相同的tails 被重复计算;更是如此,因为(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))

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

【讨论】:

【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多