Skip to content

[12장] 함수 조합 다시 보기

suguni edited this page Jan 2, 2012 · 3 revisions

함수 조합 다시 보기

복잡한 프로그램 디자인하기

문제 내에서 의존성을 언급하고 있다면 각각을 별도 함수로 만들어라.

그렇지 않으면, 디자인 레시피 이용해야 하는데 이 경우 아래 상황들이 발생할 수 있음. 1, 2번은 언급한 내용이고 3, 4번은 이 장에서 다룰 내용.

  1. 출력이 조건에 따라 다른 경우 - cond 사용
  2. 특정 영역의 지식이 필요한 경우 - 해당 영역 보조함수로 작성
  3. 임의 크기의 데이터를 사용하는 경우 - 보조함수 사용
  4. If the natural formulation of the function isn't quite what we want, it is most likely a generalization of our target. In this case, the main function is a short definition that defers the computation to the generalized auxiliary program. ??? 일반화된 보조함수를 사용

보조함수가 필요하다고 판단되면 소원 목록(WISH LIST)에 보조함수를 넣고 하나씩 완성해 나간다. 소원 목록에 넣기 전에 Scheme에서 제공하는 기본 연산과 함수에 해당 기능이 있는지 먼저 확인해라.

재귀 보조 함수

삽입정렬 함수를 만드는 과정을 통해, 세번째 경우(임의 크기 데이터 사용)를 설명

sort 함수를 만들어 보자. 먼저 계약, 목적, 헤더와 테스트(예) 만들자.

;; sort : list-of-numbers  ->  list-of-numbers 
;; to create a sorted list of numbers from all the numbers in alon
(define (sort alon) ...)

;; tests
(check-expect (sort empty) empty)
(check-expect (sort (cons 1297.04 (cons 20000.00 (cons -505.25 empty))))
              (cons 20000.00 (cons 1297.04 (cons -505.25 empty))))

임의 크기 데이터를 처리하므로 리스트 템플릿 적용

(define (sort alon)
  (cond
    [(empty? alon) ...]
    [else ... (first alon) ... (sort (rest alon)) ...]))

하위 표현식 완성하기.

먼저 (empty? alon)의 결과는 empty 이다.

else 절에서 각 표현의 의미는 아래와 같음.

  • (first alon)alon의 첫번째 값
  • (sort (rest alon))(rest alon)의 정렬된 리스트

결국 (first alon)(sort (rest alon))에 올바른 위치에 삽입되어야 하고 이를 보조함수로 만들자.

이 보조 함수를 insert라고 하면 완성된 sort 함수는 아래와 같음.

;; sort : list-of-numbers  ->  list-of-numbers 
;; to create a sorted list of numbers from all the numbers in alon
(define (sort alon)
  (cond
    [(empty? alon) empty]
    [else (insert (first alon) (sort (rest alon)))]))

이제 insert 함수 만들기. sort처럼 계약, 목적, 헤더와 테스트(예).

;; insert : number list-of-numbers  ->  list-of-numbers
;; to create a list of numbers from n and the numbers on alon 
;; that is sorted in descending order; alon is already sorted
(define (insert n alon) ...)

;; tests
(check-expect (insert 5 empty) (cons 5 empty))
(check-expect (insert 1297.04 (cons 20000.00 (cons -505.25 empty)))
              (cons 20000.00 (cons 1297.04 (cons -505.25 empty))))

'sort'와 달리 2개의 인자를 받긴 하지만, 리스트를 처리하므로 동일한 템플릿 사용

(define (insert n alon)
  (cond
    [(empty? alon) ...]
    [else ... (first alon) ... (insert n (rest alon)) ...]))

여기서 (empty? alon)의 결과는 원소가 n인 리스트이므로 (cons n empty) 이다.

else 절은 n(first alon)을 비교하여 n이 크거나 같은 경우와 작은 경우로 나눠서 결과를 만들면 된다. 즉 아래와 같은 템플릿을 적용하면 됨.

(cond
  [(>= n (first alon)) ...]
  [(< n (first alon)) ...]

위에서 첫번째는 nalon 의 앞에 붙이면 되고, 두번째의 경우는 (first alon)(rest alon)n이 합쳐진 리스트 즉, (insert n (rest alon)) 앞에 붙이면 된다.

(cond
  [(>= n (first alon)) (cons n alon)]
  [(< n (first alon)) (cons (first alon) (insert n (rest alon)))])

그래서 완성된 insert 함수는 아래와 같다.

(define (insert n alon)
  (cond
    [(empty? alon) ...]
    [else
      (cond
        [(>= n (first alon)) (cons n alon)]
        [(< n (first alon)) (cons (first alon) (insert n (rest alon)))])]))

연습문제 12.2.1 ~ 12.2.2

문제 일반화, 함수 일반화

다각형 그리기 함수를 만드는 과정을 통해, 네번째 경우 설명

먼저 posn 리스트 정의.

list-of-posns는 다음 두 가지 중 하나.
    empty : 빈 리스트인
    (cons p lop) : p는 posn 구조체, lop는 posn 리스트

그런데 posn이 하나도 없는 것(empty)은 다각형이 아니므로 위 정의는 다각형을 정의할 수 없음. 그래서 다각형을 아래와 같이 정의함

다각형은 다음 두 가지 중 하나
    (cons p empty) : p는 posn
    (cons p lop) : p는 posn, lop는 다각형

질문!!! 다각형의 정의에서 lop는 다각형이라고 되어 있는데 list-of-posns 아닌가???

암튼, 위 정의를 이용하여 리스트 템플릿을 적용한 후 함수를 완성하면 아래와 같다.

(define (draw-polygon a-poly)
  (cond
    [(empty? (rest a-poly)) true]
    [else (and (draw-solid-line (first a-poly) (second a-poly))
               (draw-polygon (rest a-poly)))]))

그런데, 문제가 있다. 위 함수를 실행하면 닫힌 도형이 아닌 열린 도형(시작점과 끝점이 연결되지 않은 도형)을 그린다. 위 함수는 다각형을 그리는 함수가 아닌 점들을 연결하는 함수인 것이다. 이 개념으로 다시 함수를 짜면 아래와 같음.

(define (draw-polygon a-poly)
  (connect-dots a-poly))

(define (connect-dots a-poly)
  (cond
    [(empty? (rest a-poly)) true]
    [else (and (draw-solid-line (first a-poly) (second a-poly))
               (draw-polygon (rest a-poly)))]))

이제 connect-dots를 호출할 때, 끝점이 맨앞에 있는 posn 리스트나 시작점이 맨끝에 있는 posn 리스트를 인자로 전달하면 됨. 이 책에서는 끝점을 맨 앞에 추가하는 방식을 설명함. 끝점을 추출하는 보조함수를 last라고 하여 draw-polygon을 다시 작성하면 아래와 같음.

(define (draw-polygon a-poly)
  (connect-dots (cons (last a-poly) a-poly)))

last 함수는 리스트 템플릿을 이용하여 쉽게 완성할 수 있다.

(define (last a-poly)
  (cond
    [(empty? (rest a-poly)) (first a-poly)]
    [else (last (rest a-poly))]))

draw-polygon 완성!

연습문제 12.3.1 ~ 12.3.2

추가 연습문제 : 단어 재배열하기

단어 정의는 아래와 같음. 이를 이용한 연습문제들

단어(word)는 다음 두 가지 중 하나
    empty
    (cons a w) : a 는 ('a, 'b, ..., 'z) 중 하나의 기호, w는 단어

연습문제 12.4.1 ~ 12.4.2