Page tree
Skip to end of metadata
Go to start of metadata

odds 구현과정 기술 

문제를 정의 한다. 

리스트에서 홀수 번째 요소들을 담고 있는 리스트를 리턴하는 function odds 를 구현한다.

데이터의 인풋 아웃풋 을 아래와 같이 정의 한다.

인풋아웃풋
빈리스트nil
(1)(1)

(1 2)

 (1)
(1 2 3)(1 3)
(1 2 3 4 5 6 7 8 9) (1 3 5 7 9)
  

function odds 는 리스트를 파라메터로 받아 결과리스트를 리턴한다.

클로저를 통해 구현 

제약사항 및 이미 알고 있는 부분들

스터디를 진행 해 오면서 익힌 내용을 기반으로 구현해보자... (족쇄차기 .. 모래주머니 효과)

본 구현 과정은 아래와 같은 내용에 대해 선 지식이 있는 걸 전제로 함..

테스트 작성부터 먼저

테스트를 먼저 만들고 이를 구현한다 ..

작은 단위를 유지하고 마음에 안정을 찾는다. 

?? 클로저에서는 테스트케이스를 어떻게 ????

일단 알고 있는 지식 선에서 테스트케이스할수 있도록 만들어보자.

자바에서 사용하던 junit 처럼 만들어 볼까??? 헛짓 말고...

일단 이구현과정에서 필요한 부분만 만들어서 진행하자....  (배끼고 또 배껴보자...)

지금 필요한 것은 실행결과 값과 희망하는 값이 같은지 만 비교하여 참은 리턴하고 거짓 일 경우 값을 콘솔에 찍고 false 를 리턴한다.

(t 1 1) true

-> sucess  

(t 1 0) false

-> fail actual 1 expect 0 

테스트를 위한 t 에 대해서는 아래 와 같이 구현 한다.....

(defn t [actual expected]
    (if (= actual expected) 
        (do 
           (println 'sucess)
         true ) 
       (do 
           (println (str 'fail!! " actual: " actual " expected " expected ))
         false)))
(t 1 1)
(t 1 0)

 

본격적인 odds 구현 

첫번째 통과 대상 

경계부터 테스트 하도록 해보자..... 오류는 대부분(question) 경계에서 생기기 땜시....

빈리스트 에 경우 nil 을 리턴해야한다. 

(t (odds ()) nil)

eval 해보면.. odds 가 없다고....

여튼 빨리 통과해보자....

(defn odds [coll]
     (cond 
         (empty? coll) nil))

(t (odds ()) nil)

 

별 볼것 없다 .에게게..... 이게 머야 정도????

그래도 최소한 odds 가 빈 리스트에 대해서는 잘 동작한다고 자부 할 수 있다.

if 보다 cond 를 사용하였다...

두번째 테스트 

nil -> nil

(t (odds nil) nil)
(defn odds [coll]
     (cond 
       (empty? coll) nil))
(t (odds ()) nil)
(t (odds nil) nil)

테스트 만 추가하고 eval 해보았는데 성공...... 

음 빈 리스트와 nil 에 대해서는 역시나 잘 동작하는 odds 를 만들었군...

아직까지 시시하다....

세번째 테스트 

(1) -> (1)

전달하는 원소 하나 짜리 (1) 가 그대로 리턴되어야 한다. 

(t (odds '(1)) '(1))
(defn odds [coll]
     (cond 
       (empty? coll) nil
       (empty? (rest coll)) coll))
(t (odds ()) nil)
(t (odds nil) nil)
(t (odds '(1)) '(1))

 

그냥 :else 절에서 coll  을 리턴 할 수 도 있다...

나름 위에 nil 을 리턴하는 조건문에  empty? 를 써보았는데 ... 다시한번 ..... 깔맞춤을......

최대한 중복을 만들아 나가는게 좋다..(중복 보다 패턴이 맞나...)

코드를 개선 해볼 만한 여지가 있나?? 아직은 잘 모르겠으니 패스....

 

4번째 테스트

(1 2) -> (1)

(t (odds '(1 2)) '(1))

결과는 당연히 실패 

왜 실패했는지 빠르게 집중해서 보자.... 

조건 문에 걸치지 않고 리턴하기 때문 ...

최대한 빨리 성공해보자..

이전 테스트에서 경험을 살려 ....

요소가 2개 일때 2개의 요소중 첫번째 걸 리턴하면된다.

다르게 생각해보면 (1 2) 일 때 (1) 을 리턴하면되니 (1 2) 의 첫번째 (1) 을 리턴하면 된다.

 

(defn odds [coll]
     (cond 
       (empty? coll) nil
       (empty? (rest coll)) coll
       (empty? (rest (rest coll))) (first coll)))
(t (odds ()) nil)
(t (odds nil) nil)
(t (odds '(1)) '(1))
(t (odds '(1 2)) '(1))

앗 될 줄 알았는데 실패......

콘솔로그를 보니 ... actual 은 1 이고 기대하는 바는 (1) 

(defn odds [coll]
     (cond 
       (empty? coll) nil
       (empty? (rest coll)) coll
       (empty? (rest (rest coll))) (cons (first coll) '())))
(t (odds ()) nil)
(t (odds nil) nil)
(t (odds '(1)) '(1))
(t (odds '(1 2)) '(1))

코드를 개선 해 볼 여지가 있나???

아직 도 잘모르겠네..

 

5번째 테스트

(1 2 3) -> (1 3)

드디여  앞선 테스트에 비해 일반적인 케이스의 경계에 서다.....

(t (odds '(1 2 3)) '(1 3))

지금은 이전에 도 말했지만 머리속 스마트함을 지워라...

이쯤되면 바보처럼 이전에 했던 식으로 조건 문은 작성할수 있다...

얼핏 스치는 생각은 조건문 작성이 먼간 패턴이 보인다. 

리스트의 크기가 0개이면 rest 0번

리스트 크기가 1개이면 rest 1번

리스트 크기가 1개이면 rest 2번

리스트 크기가 n개이면 rest n번 인듯 싶다.....

그런데  조건문 만족 시 실행 구문 에 대해서는 느낌이 오지는 않는다...

고로 머리쓰지 않고 무식하게 나간다. 

(1 2 3) -> (1 3) 이여여하므로 첫번째 원소와 맨마지막 원소를 가지고 리스트를 구성하여 리턴하도록한다. 

첫번째 원소는 first 를 이용하고 맨 마지막 원소는 rest  를 두번 하면 될 듯 싶다. 

일단 구현 

(defn odds [coll]
     (cond 
       (empty? coll) nil
       (empty? (rest coll)) coll
       (empty? (rest (rest coll))) (cons (first coll) '()) 
       (empty? (rest (rest (rest coll)))) (cons (first coll) (rest (rest coll))) ))
(t (odds ()) nil)
(t (odds nil) nil)
(t (odds '(1)) '(1))
(t (odds '(1 2)) '(1))
(t (odds '(1 2 3)) '(1 3))

해놓고 보니 먼가 장황하고 먼가 좀 그러네.... 

3번째 조건에 대한 구문 을 이런 식으로 변경도 가능 할 듯 싶다. 

coll -> (cons (first coll) '())

(1)의  첫번째 를 가지고 리스트를 만들면 (1) 

(defn odds [coll]
     (cond 
       (empty? coll) nil
       (empty? (rest coll)) (cons (first coll) '()) 
       (empty? (rest (rest coll))) (cons (first coll) '()) 
       (empty? (rest (rest (rest coll)))) (cons (first coll) (rest (rest coll))) ))
(t (odds ()) nil)
(t (odds nil) nil)
(t (odds '(1)) '(1))
(t (odds '(1 2)) '(1))
(t (odds '(1 2 3)) '(1 3))

음 감이 적중했네....

조건문의 실행문 또한 유사한 형태로 변경 가능 .....중복이 발생 

리스트의 크기가 4개 인 것 까지 해보고 동일한 실행문의 패턴이 나오면 :else 를 사용 할 수 있지 않을 까???

(defn odds [coll]
     (cond 
       (empty? coll) nil
       (empty? (rest coll)) (cons (first coll) (rest (rest coll))) 
       (empty? (rest (rest coll))) (cons (first coll) (rest (rest coll))) 
       (empty? (rest (rest (rest coll)))) (cons (first coll) (rest (rest coll))) ))
(t (odds ()) nil)
(t (odds nil) nil)
(t (odds '(1)) '(1))
(t (odds '(1 2)) '(1))
(t (odds '(1 2 3)) '(1 3))

6번 째 테스트 

(1 2 3 4)  -> (1 3)

(t (odds '(1 2 3 4)) '(1 3))

일단 앞에서 감잡은 두가지 를 적용시켜 테스트를 돌려 보자.

첫번째 감 리스트 크기가 n 이면 rest n번으로 퉁칠수 있다.

두번째 감 (cons (first coll) (rest (rest coll))) 는 동일 할 듯 싶은데???

언제까지나 empty? 와 rest 를 써야 하는게 한심하게 느껴지지만 난 소심하고 안전빵을 좋아하니 더 지켜보기로하자...

(defn odds [coll]
     (cond 
       (empty? coll) nil
       (empty? (rest coll)) (cons (first coll) (rest (rest coll))) 
       (empty? (rest (rest coll))) (cons (first coll) (rest (rest coll))) 
       (empty? (rest (rest (rest coll)))) (cons (first coll) (rest (rest coll))) 
       (empty? (rest (rest (rest (rest coll))))) (cons (first coll) (rest (rest coll))) ))
(t (odds ()) nil)
(t (odds nil) nil)
(t (odds '(1)) '(1))
(t (odds '(1 2)) '(1))
(t (odds '(1 2 3)) '(1 3))
(t (odds '(1 2 3 4)) '(1 3))

위 코드는 마지막 테스트 에서 실패 

일단 결과가 이전 테스트의 (1 3) 과 동일 즉 (1 2 3 4) 에서 마지막 4을 선택 하지 않게 변경 

이전 테스트에서는  rest 로 리스트를 까고 남는 놈을 첫번째 놈과 cons 하였으나 

이번 케이스에서는 그렇게 하면 (1 3 4) 가 됨 rest 두번 까고 첫번째 놈과 cons 하니 당연한 결과 .....

이번에는 rest 로 두번 까고 그 리스트 (3 4) 의 첫번째를 취한 형식을 잽싸게 테스트 를 통과해본다.

 

(defn odds [coll]
     (cond 
       (empty? coll) nil
       (empty? (rest coll)) (cons (first coll) (rest (rest coll))) 
       (empty? (rest (rest coll))) (cons (first coll) (rest (rest coll))) 
       (empty? (rest (rest (rest coll)))) (cons (first coll) (rest (rest coll))) 
       (empty? (rest (rest (rest (rest coll))))) (cons (first coll) (cons (first (rest (rest coll))) '()) ) ))
(t (odds ()) nil)
(t (odds nil) nil)
(t (odds '(1)) '(1))
(t (odds '(1 2)) '(1))
(t (odds '(1 2 3)) '(1 3))
(t (odds '(1 2 3 4)) '(1 3))

다행이도 성공 

코드를 뚫어지게 보고 감을 다시 잡아 본다.

 

(defn odds [coll]
     (cond 
       (empty? coll) nil
       (empty? (rest coll)) (cons (first coll) (odds (rest (rest coll))))
       (empty? (rest (rest coll))) (cons (first coll) (odds (rest (rest coll)))) 
       (empty? (rest (rest (rest coll)))) (cons (first coll) (odds (rest (rest coll)))) 
       (empty? (rest (rest (rest (rest coll))))) (cons (first coll) (odds (rest (rest coll))) )))
(t (odds ()) nil)
(t (odds nil) nil)
(t (odds '(1)) '(1))
(t (odds '(1 2)) '(1))
(t (odds '(1 2 3)) '(1 3))
(t (odds '(1 2 3 4)) '(1 3))

:else 퉁 가능 할듯 ...한번 고쳐보고 테스트들을 돌려본다.

마지막 테스트 

(1 2 3 4 5 6 7 8 9) -> (1 3 5 7 9)

(t (odds '(1 2 3 4 5 6 7)) '(1 3 5 7))

 

(defn odds [coll]
       (cond 
         (empty? coll) nil
         :else (cons (first coll) (odds (rest (rest coll))))))
(t (odds ()) nil)
(t (odds nil) nil)
(t (odds '(1)) '(1))
(t (odds '(1 2)) '(1))
(t (odds '(1 2 3)) '(1 3))
(t (odds '(1 2 3 4)) '(1 3))
(t (odds '(1 2 3 4 5 6 7)) '(1 3 5 7))

 

마지막 테스트 성공... 

 

느낀점

물론 현업에서 이런 간단한 odds  를 작성 할 일은 없다..... 넘쳐나는 유용한 라이브러리들을 잘 조합하는데 중점을 두겠지.....

하지만 위 와 같은 사고의 흐름을 자신으로 것으로 만드는 것은 쉽지는 않다고 생각한다...

끊임없는 타이핑과 사고 그리고 시간 더불어 끈기가 필요할것이다..... 

  • No labels

5 Comments

  1. 이런 식의 접근 방식을 공유하는 것 정말 좋다. 특정 지식을 전달하는 것보다는 이런 방식으로 사고하고 문제를 해결해 나가는 과정을 공유하는 것은 정말 중요하다고 생각한다. 남은 내용도 기대할께.

  2. 박재성 글 쓰는 거 넘어려워요 ㅜㅜ 

  3. 가능하다면 다음 스터디가 테스트이니 시작전에 짧게 이야기하고 지나가도 좋을 것 같아요!!!

    1. 좋은 생각이다. 이 문서에 정리한 내용을 스터디 시작하는 시점에 공유해 주면 좋겠다. 내가 자리 마련할께.

  4. 완수형 ㄷㄷ;;;; 대단하십니다 ㄷㄷ;;;;;