Separation seq - Recursion in Clojure (or Lisp in general)

In the project that I am working on, I came across an interesting problem that I am interested in other solutions about. I'm in the middle of reading The Little Schemer, so I'm trying some recursion methods. I am wondering if there is another way to do this with recursion, and is also interested if there is an approach without using recursion.

The task is to take a sequence and break it into seq from seqs, taking every nth element. For example, this vector:

[ :a :b :c :d :e :f :g :h :i ] 

when divided by n = 3 will generate seq

 ((:a :d :g) (:b :e :h) (:c :f :i)) 

and with n = 4:

 ((:a :e :i) (:b :f) (:c :g) (:d :h)) 

etc. I solved this using two functions. The first creates internal seqs, and the other combines them. Here are my functions:

 (defn subseq-by-nth "Creates a subsequence of coll formed by starting with the kth element and selecting every nth element." [coll kn] (cond (empty? coll) nil (< (count coll) n) (seq (list (first coll))) :else (cons (nth coll k) (subseq-by-nth (drop (+ nk) coll) 0 n)))) (defn partition-by-nth "" ([coll n] (partition-by-nth coll nn)) ([coll ni] (cond (empty? coll) nil (= 0 i) nil :else (cons (subseq-by-nth coll 0 n) (partition-by-nth (rest coll) n (dec i)))))) 

I am not entirely happy that the "partitioning" function has multiple arity just for recursion, but could not see another way.

This seems to work fine with all test cases. Is this a decent approach? Is it too complicated? Is there a way to do this without recursion, or perhaps in a single recursive function?

Thanks for the suggestions. I am new to both Clojure and Lisp, so I collect various methods when I go.

+6
source share
3 answers

I expect there is a simpler recursive definition, which is more like The Little Schemer, but the following function using take-nth pretty compact as you said you were interested in alternative approaches:

 (defn chop [coll n] (for [i (range n)] (take-nth n (drop i coll)))) 

which satisfies your examples:

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

The built-in libraries in Clojure will delight you amazingly; when this fails, use an explicitly recursive solution. This version is also lazy; you probably want to use lazy-seq or loop...recur in any "long" (explicitly recursive) version to process large datasets without inflating the stack.

+9
source

Edited because the original answer completely missed the point.

When I first saw this question, I thought that the clojure.core function partition is being applied (see the ClojureDocs page ).

As Dave noted, the section only works on elements in the original order. The take-nth solution is clearly better. Just for fun, a card combination with several sequences derived from section views.

 (defn ugly-solution [coll n] (apply map list (partition nn (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)) 
0
source

I have to offer this general Lisp loop:

 (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 in))) :finally (return (map 'list #'nreverse result)))) 
0
source

Source: https://habr.com/ru/post/927311/


All Articles