2012-10-10 2 views
6

프로젝트에서 나는 다른 솔루션에 대해 궁금한 흥미로운 문제를 발견했다. 나는 "The Little Schemer"를 읽는 중이다. 그래서 나는 재귀 기술을 시험 중이다. 재귀를 사용하여이 작업을 수행하는 또 다른 방법이 있는지, 그리고 재귀를 사용하지 않고 접근법이 있는지 궁금합니다.Clojure (또는 일반적으로 Lisp)에서 seq - Recursion 분할하기

문제는 시퀀스를 취하여 모든 n 번째 요소를 취하여 seq의 seq로 분할하는 것입니다. 예를 들어,이 벡터 :

서열을

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

와 함께 N = 4를 생성 할 N = 3으로 분할

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

:

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

등등. 두 가지 기능을 사용하여이 문제를 해결했습니다. 첫 번째는 내부 seq를 만들고 다른 하나는 내부 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 번째의 기능을 완전히 행복하지 않다,하지만 다른 방법을 볼 수 없었다.

모든 테스트 사례에서 제대로 작동하는 것 같습니다. 이것은 괜찮은 접근인가? 너무 복잡합니까? 재귀 또는 일회성 재귀 함수없이이 작업을 수행 할 수있는 방법이 있습니까?

제안 해 주셔서 감사합니다. 나는 Clojure와 Lisp에 익숙하지 않기 때문에 다른 기술을 선택하고있다.

답변

9

좀 더 의 정신에 작은 음모 수있는 간단한 재귀 정의가 예상하지만, 당신은 당신이 다른 방법에 관심이 말했다 이후 take-nth을 사용하여 다음과 같은 기능이 아주 조금 더 컴팩트 :

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

은 예 만족 : Clojure의에서

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

, 도서관에 내장 된이 의외로 지금까지 당신을 얻을 것이다; 실패 할 경우 명시 적 재귀 적 솔루션을 사용하십시오. 이 버전은 또한 게으르다; 스택을 부 풀리지 않고 큰 데이터 세트를 처리하기 위해 "길이"(명시 적으로 재귀 적) 버전에 lazy-seq 또는 loop...recur을 사용하고 싶을 것입니다.

+0

감사합니다, 그것은 굉장합니다!나는 그것이 그렇게 쉬울 수 있는지 전혀 몰랐다. 내가 이것을 이해하기 시작하고 있다고 생각할 때 나는 정말로 내가 얼마나 작은지를 보여 주었다. –

+0

나는 그 감각을 알고있다! – JohnJ

+0

@DaveKincaid : 재귀를 사용하는 대신 기존의 고차 함수를 사용하여 솔루션을 모델링해야하며이 종류의 간결한 솔루션을 제공 할 수 있어야합니다. – Ankur

0

원본 답변이 완전히 누락 되었기 때문에 편집되었습니다.

이 질문을 처음 보았을 때 clojure.core 함수 partition이 적용된 것으로 생각했습니다 ( ClojureDocs page 참조).

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)) 
+0

나는 그것을 보았지만이 경우에 그것을 어떻게 사용 하는지를 알 수 없었다. 항상 같은 순서로 요소를 분할하는 것처럼 보였습니다. 당신이 그것을 할 수있는 방법이 있다면, 나는 그것을 보는 것에도 흥미가있을 것입니다. –

+0

@DaveKincaid 네, 당신 말이 맞습니다. 재정렬 요구 사항은 파티션이 작동하지 않음을 의미합니다. 나는 파티션을 사용하는 해답을 편집했지만 take-nth는 확실히 더 좋은 방법이다. –

0

나는이 커먼 리스프 루프를 제공하는 :

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