我是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)
并不能解决问题。
答案 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),而且我认为这与递归的使用有关。通常,您会使用诸如reductions
,partition-by
,split-at
之类的东西,以便执行此类操作。我觉得也应该有一种方法可以使它成为一个转换器,并从处理过程中分离出惰性(因此您可以使用sequence
来表示您很懒惰),但是我没有时间来工作现在出来。我将尽力尽快提供更完整的答案。