如何在Clojure中将序列的分区(带串联分解)定义为惰性序列的惰性序列

时间:2019-04-27 13:40:44

标签: clojure functional-programming concatenation factorization lazy-sequences

我是Clojure的新手,我想定义一个函数pt,以一个数字n和一个序列s作为参数,并在其中返回s的所有分区n个部分,即相对于n级联的因式分解。例如(pt 3 [0 1 2])应该产生:

(([] [] [0 1 2]) ([] [0] [1 2]) ([] [0 1] [2]) ([] [0 1 2] []) ([0] [] [1 2]) ([0] [1] [2]) ([0] [1 2] []) ([0 1] [] [2]) ([0 1] [2] []) ([0 1 2] [] []))

,顺序不重要。 具体来说,我希望结果是向量的惰性序列的惰性序列。

我对这种功能的第一次尝试是:

(defn pt [n s]
  (lazy-seq
    (if (zero? n)
      (when (empty? s) [nil])
      ((fn split [a b]
         (concat
           (map (partial cons a) (pt (dec n) b))
           (when-let [[bf & br] (seq b)] (split (conj a bf) br))))
       [] s))))

之后,我写了一个不太简洁的版本,通过避免对1部分分区进行无用的比较来降低时间复杂度,如下所示:

(defn pt [n s]
  (lazy-seq
    (if (zero? n)
      (when (empty? s) [nil])
      ((fn pt>0 [n s]
         (lazy-seq
           (if (= 1 n)
             [(cons (vec s) nil)]
             ((fn split [a b]
                (concat
                  (map (partial cons a) (pt>0 (dec n) b))
                  (when-let [[bf & br] (seq b)] (split (conj a bf) br))))
              [] s))))
       n s))))

这些解决方案的问题在于,尽管它们起作用,但它们会产生(非懒惰)缺点的懒惰序列,我怀疑必须采取完全不同的方法来实现“内部懒惰”。因此,欢迎进行任何更正,建议,解释!

编辑:在阅读l0st3d的答案后,我想我应该明确一点,就是我不想让分区仅仅是LazySeq而是“真正的懒惰”,就意味着要计算一部分并且仅在需要时才保留在内存中。 例如,下面给出的两个函数都产生LazySeq,但是只有第一个产生“真正的惰性”序列。

(defn f [n]
  (if (neg? n)
    (lazy-seq nil)
    (lazy-seq (cons n (f (dec n))))))
(defn f [n]
  (if (neg? n)
    (lazy-seq nil)
    (#(lazy-seq (cons n %)) (f (dec n)))))

因此,映射(partial concat [a])#(lazy-seq (cons a %))而不是(partial cons a)并不能解决问题。

1 个答案:

答案 0 :(得分:0)

cons内联fn中的split调用是唯一引入渴望的地方。您可以将其替换为懒散地构造列表的内容,例如concat

(defn pt [n s]
  (lazy-seq
   (if (zero? n)
     (when (empty? s) [nil])
     ((fn split [a b]
        (concat
         (map (partial concat [a]) (pt (dec n) b))
         (when-let [[bf & br] (seq b)] (split (conj a bf) br))))
      [] s))))

(every? #(= clojure.lang.LazySeq (class %)) (pt 3 [0 1 2 3])) ;; => true

但是,阅读代码后,我觉得这完全不是克洛瑞(Clojurey),而且我认为这与递归的使用有关。通常,您会使用诸如reductionspartition-bysplit-at之类的东西,以便执行此类操作。我觉得也应该有一种方法可以使它成为一个转换器,并从处理过程中分离出惰性(因此您可以使用sequence来表示您很懒惰),但是我没有时间来工作现在出来。我将尽力尽快提供更完整的答案。