对seq进行分区 – Clojure中的递归(或一般的Lisp)

在我正在研究的一个项目中,我遇到了一个有趣的问题,我对其他解决方案感到好奇.我正在阅读“The Little Schemer”,所以我正在尝试一些递归技术.我想知道是否有另一种方法可以通过递归来实现这一点,并且如果有一种不使用递归的方法也感兴趣.

问题是通过获取每个第n个元素来获取序列并将其划分为seqs序列.例如这个向量:

[ :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))

等等.我用两个函数解决了这个问题.第一个创建内部seqs,另一个将它们拉在一起.这是我的功能:

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

我对于具有多个arity的partition-by-nth函数仅仅对于递归感到不满意,但是看不到另一种方式.

对于所有测试用例,这似乎都可以正常工作.这是一个不错的方法吗?太复杂了吗?有没有办法在没有递归或可能在单个递归函数中执行此操作?

谢谢你的建议.我是Clojure和Lisp的新手,所以我正在采用不同的技术.

我希望有一个更简单的递归定义更符合The Little 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中,内置的库将让您惊喜不已;当失败时,使用显式递归解决方案.这个版本也很懒;你可能想在任何“longhand”(显式递归)版本中使用lazy-seq或loop … recur来处理大型数据集而不会破坏堆栈.

本站公众号
   欢迎关注本站公众号,获取更多程序园信息
开发小院