Recursion 在Clojure(或一般的Lisp)中对seq递归进行分区
在我正在进行的一个项目中,我遇到了一个有趣的问题,我对其他解决方案很好奇。我正在阅读“小阴谋家”,所以我正在尝试一些递归技术。我想知道是否有另一种方法可以使用递归来实现这一点,我还想知道是否有一种方法不使用递归 问题是获取一个序列,并通过获取每个第n个元素将其划分为一个序列中的序列。例如,该向量:Recursion 在Clojure(或一般的Lisp)中对seq递归进行分区,recursion,clojure,lisp,Recursion,Clojure,Lisp,在我正在进行的一个项目中,我遇到了一个有趣的问题,我对其他解决方案很好奇。我正在阅读“小阴谋家”,所以我正在尝试一些递归技术。我想知道是否有另一种方法可以使用递归来实现这一点,我还想知道是否有一种方法不使用递归 问题是获取一个序列,并通过获取每个第n个元素将其划分为一个序列中的序列。例如,该向量: [ :a :b :c :d :e :f :g :h :i ] 当使用n=3进行分区时,将生成seq ((:a :d :g) (:b :e :h) (:c :f :i)) n=4时: ((:a :e
[ :a :b :c :d :e :f :g :h :i ]
当使用n=3进行分区时,将生成seq
((:a :d :g) (:b :e :h) (:c :f :i))
n=4时:
((:a :e :i) (:b :f) (:c :g) (:d :h))
等等。我用两个函数解决了这个问题。第一个创建内部seq,另一个将它们拉到一起。以下是我的职能:
(defn subseq-by-nth
"Creates a subsequence of coll formed by starting with the kth element and selecting every nth element."
[coll k n]
(cond (empty? coll) nil
(< (count coll) n) (seq (list (first coll)))
:else (cons (nth coll k) (subseq-by-nth (drop (+ n k) coll) 0 n))))
(defn partition-by-nth
""
([coll n]
(partition-by-nth coll n n))
([coll n i]
(cond (empty? coll) nil
(= 0 i) nil
:else (cons (subseq-by-nth coll 0 n) (partition-by-nth (rest coll) n (dec i))))))
(n次定义)
“通过从第k个元素开始并选择每个第n个元素来创建coll的子序列。”
[coll k n]
(秒(空?科尔)零
(<(计数列)n)(序号(列表列(第一列)))
:其他(cons(第n列第k列)(第n列第q列第n列第(n列第k列第0列)))
(按n定义分区)
""
([coll n]
(按第n列划分)
([coll n i]
(秒(空?科尔)零
(=0 i)无
:else(cons(subseq被n个coll 0 n分割)(被n个(rest coll)n分割(dec i‘‘‘‘‘‘‘‘)
我不太喜欢N次函数的分区,因为它对于递归具有多重算术性,但我看不到其他方法
这似乎在所有测试用例中都能正常工作。这是一个体面的方法吗?是不是太复杂了?有没有一种方法可以不用递归,或者用一个递归函数来实现
谢谢你的建议。我对Clojure和Lisp都是新手,所以我会在学习的过程中学习不同的技术。我希望有一个更简单的递归定义,它更符合小Schemer的精神,但是下面使用
take nth
的函数更加紧凑,因为您说过您对其他方法感兴趣:
(defn chop [coll n]
(for [i (range n)]
(take-nth n (drop i coll))))
这满足了您的示例:
(chop [:a :b :c :d :e :f :g :h :i ] 3)
;= ((:a :d :g) (:b :e :h) (:c :f :i))
(chop [:a :b :c :d :e :f :g :h :i ] 4)
;= ((:a :e :i) (:b :f) (:c :g) (:d :h))
在Clojure中,内置库将让您出人意料地走得更远;如果失败,请使用显式递归解决方案。这个版本也是懒惰的;您可能希望在任何“正手”(显式递归)版本中使用
lazy seq
或loop…recurr
,以处理大型数据集,而不破坏堆栈。编辑,因为原始答案完全没有抓住要点
当我第一次看到这个问题时,我认为clojure.core函数partition
已应用(请参阅
)
正如Dave所指出的,分区只对原始顺序的元素起作用。take-nth
解决方案显然更好。仅仅是为了兴趣,一个组合的地图与多个序列衍生自分区类作品
(defn ugly-solution [coll n]
(apply map list (partition n n (repeat nil) coll)))
(ugly-solution [:a :b :c :d :e :f :g :h :i] 3)
;;=> ((:a :d :g) (:b :e :h) (:c :f :i))
(ugly-solution [:a :b :c :d :e :f :g :h :i] 4)
;;=> ((:a :e :i) (:b :f nil) (:c :g nil) (:d :h nil))
我必须提供这个常见的Lisp循环:
(defun partition-by-nth (list n)
(loop :with result := (make-array n :initial-element '())
:for i :upfrom 0
:and e :in list
:do (push e (aref result (mod i n)))
:finally (return (map 'list #'nreverse result))))
谢谢,太棒了!我不知道会这么容易。就在我开始理解这一点的时候,我发现我实际上做的很少。@Davekingaid:你应该尝试使用现有的高阶函数而不是使用递归来建模你的解决方案,你肯定能够想出这种简洁的解决方案:)我通常会这样做,但是,如果您以前没有使用过它,那么就很难找到您想要的特定功能。看起来我所做的是重新实现take n函数。这不是我第一次实现核心Clojure函数。至少每一个都是一次很好的学习经历。这是一个画家Shlemiel,不是吗?我看到了,但不知道如何在这种情况下使用它。它似乎总是将元素保持在相同的顺序。“如果你有办法的话,我也很想看一看。”“是的,你说得很对。”。重新排序要求意味着分区不起作用。我已经编辑了使用分区的答案,但是使用n绝对是更好的方法。