2015년 8월 28일 금요일

Living Clojure -- Week 1, Day 5

이번에도 모두 2일차 진행하며 풀어봤던 문제들이다. 한번에 쭈욱~ 달릴 수 있는 것을 길게 늘여놓았구나 싶다.

문제 30, 이어진 중복 제거.
(= (apply str (__ "Leeeeeerrroyyy")) "Leroy")

loop/recur로 풀수 있는 문제지만 accumulator의 last를 비교해야 한다는 점이 조금 거슬린다. 이건 아마도 Haskell느낌? 하지만 Clojure의 vector는 last도 효율적이니까!
(fn [list]
  (loop [list list
         acc []]
    (if (empty? list)
      acc
      (if (= (last acc) (first list))
        (recur (rest list) acc)
        (recur (rest list) (conj acc (first list)))))))

이미 있는 sequence함수를 사용한다면 reduce로 쉽게 구현된다.

reduce (fn[a b](if (= (last a) b) a (conj a b))) []

그런데 만약 map/filter 등의 sequence함수들처럼 lazy sequence를 반환해야 한다면 어떻게 할 수 있을까? 단순히loop/recur + accumulator로는 안될 것이다.

Rx의 distinctUntilChanged와 같다! 직접 마지막 요소를 keep해도 되지만 drop-while을 이용하면 편리하다. 그리고 오히려 더 간단하다!

(fn duc[coll]
  (lazy-seq
   (when-let [s (seq coll)]
     (let [f (first s)]
       (cons f (duc (drop-while #(= % f) (rest s))))))))

laziness를 확인하려면 함수에 (range) 와 같은 무한시퀀스를 적용시키고 앞에서 (take 5)처럼 가져오면 된다. 첫번째 코드는 undefined이며 두번째는 잘 동작한다.


31번, 이어진 같은 값 묶기.
(= (__ [1 1 2 1 1 1 3 3]) '((1 1) (2) (1 1 1) (3 3)))

아무 생각없이 loop/recur로 작성하면 된다. 이 문제는 내가 가끔 연습문제로 소개하는 개미수열의 하위 문제이기도 하다. (take-while/drop-while)을 이용하면 간단하다. 물론 자주 등장하기 때문에 (split-with)가 이미 있다!

이문제도 lazy sequence를 반환하도록 하려면 lazy-seq을 이용해야 한다.
(fn pack[coll]
  (lazy-seq
   (when-let [s (seq coll)]
     (let [f (first s)
           [hd tl] (split-with #(= % f) s)]
       (cons hd (pack tl))))))

그런데 Code Golf를 보니 엄청 짧은 답들이 많다. 어떻게 한 것일까?

laziness 제거하고 77
(fn p[c]
  (when-let[s (seq c)]
    (let [f (first s)
          [t d](split-with #(= % f) s)]
      (cons t (p d)))))

50글자 이내의 답들이 있는 걸보면 vector를 가정한 것이 아닐까 싶다.

67글자
(fn p[c](if(seq c)(let[[t d] (split-with #(= % (c 0)) c)](cons t(p (vec d))))))

에구, 더는 안되겠다!


41번, n번째마다 버리기.
(= (__ [1 2 3 4 5 6 7 8] 3) [1 2 4 5 7 8])

이건 오히려 loop/recur가 더 어렵겠다. 이미 있는 partition(-all)을 이용하면 간단하다.
(fn[c n](apply concat (partition-all (dec n) n c)))


45번, iterate.
(= __ (take 5 (iterate #(+ 3 %) 1)))

간단하니 넘어가고..


33번, replicate.
(= (__ [1 2 3] 2) '(1 1 2 2 3 3))

두번씩 반복한 32번 문제의 일반화!
(fn[c n](mapcat #(repeat n %) c))            



이렇게 1주일이 지났다. 함수형 프로그래밍 언어들의 연습문제들이 서로 비슷하다. 비슷한 문제들이기 때문에 언어적인 부분을 비교해보기 좋다.

댓글 없음:

댓글 쓰기