sicp 2.2 계층 구조 데이터와 닫힘 성질
TRANSCRIPT
상자와 화살표 쌍을 나타내기
닫힘 성질로 계층 구조표현 가능
- 차례열(Sequence)
- 나무(Tree)
- 그림 언어(Graphic)
2.2.1 차례열(Sequence) 표현 방법
(cons ⟨a1⟩
(cons ⟨a2⟩
(cons : : :
(cons ⟨an⟩
nil): : :)))
(list ⟨a1⟩ ⟨a2⟩ : : : ⟨an⟩)
리스트 - Cons를 겹쳐 만든 쌍의 차례열
(define one-through-four (list 1 2 3 4))
• one-through-four
- (1 2 3 4)
• (car one-through-four)
- 1
• (cdr one-through-four)
- (2 3 4)
• (car (cdr one-through-four))
- 2
• (cons 10 one-through-four)
- (10 1 2 3 4)
리스트 연산
• ‘cdr연산을 가지고 흝어 내려가는’ 프로그램 기법으로 리스트 연산을 만듬.
• List-ref 프로시져 - 리스트와 수 N을 인자로 받아서, 그 리스트의 n번째 원소를 내놓음.
• List-ref 규칙
– N = 0이면, list-ref의 값은 리스트의car 이다.
– 그렇지 않으면, list-ref는 리스트의cdr에서 n-1번째 원소다.
(define (list-ref items n)
(if (= n 0)
(car items)
(list-ref (cdr items) (- n 1))))
(define squares (list 1 4 9 16 25))
(list-ref squares 3)
16
리스트 연산
• length 프로시져 - 리스트를 인자로받아 리스트의 원소가 몇 개인지 찾음.
• length 규칙– 리스트의 length는 그 리스트의 cdr
의 length에 1을 더한 값이다.
– 빈 리스트의 length는 0이다.
• Null? – 인자가 빈인자인지 판단.
(define (length items)
(if (null? items)
0
(+ 1 (length (cdr items)))))
(define odds (list 1 3 5 7))
(length odds)
4-----------------------------------------------(define (length items)
(define (length-iter a count)
(if (null? a)
count
(length-iter (cdr a) (+ 1 count))))
(length-iter items 0))
(define odds (list 1 3 5 7))
(length odds)
리스트 연산
• ‘리스트 구조를 cdr로 풀어 헤치면서, 이를 cons로 되묶어서 새로운 리스트를 만드는 방법’
• Append 프로시져 – 두 리스트를 인자로 받아 모든 원소를 한데 엮어 새 리스트를 만듬.
• Append 규칙
– List1이 빈 리스트면, 결과는 list2임.
– 그렇지 않으면, list1의 cdr와 list2를append한 다음 , 그 결과에 list1의car를 cons 한 리스트를 만듬.
(define squares (list 1 2 3 4))
(define odds (list 6 7 8 9))
(define (append list1 list2)
(if (null? list1)
list2
(cons (car list1)
(append (cdr list1) list2))))
(append squares odds)
- 1 2 3 4 6 7 8 9
리스트 매핑(Mapping)
• 리스트 연산에서 계산 방법을 요약해서 차수높은 프로시져(Higher-Order Procedure )를만든것이 Map.
• Map – 프로시져 하나와 리스트를인자로 받아, 리스트 원소마다 똑같은 프로시져를적용한 결과를 묶은리스트를 반환.
(define (scale-list items factor)(if (null? items)
null(cons (* (car items) factor)
(scale-list (cdr items)factor))))
(scale-list (list 1 2 3 4 5) 10)
(10 20 30 40 50)
---------------------------------(define items1 (list 1 2 3 4 5))(define factor1 10)
(define (scale-list1 items factor)(map (lambda (x) (* x factor))items))
(scale-list1 items1 factor1)
2.2.2 계층 구조
• 차례열을 원소로하는 차례열을나타낼수 있음.
(cons (list 1 2) (list 3 4))
• 차례열을 원소로 가지는 차례열을 나무(Tree) 꼴로 펼쳐 나타낼수있음.
Count-leaves 프로시져
• Count-leaves 프로시져 - 재귀 처리로 나무 가지를 순회하여 나뭇잎의카운터를 계산.
• Count-leaves 규칙
– 리스트 x의 length는 x의 cdr의length에 1을 더한 값이다.
– 빈 리스트의 length는 0이다.
• Pair 프로시져 – 인자로 받은 값이 쌍인지판단.
#lang scheme ;added
(define x (cons (list 1 2) (list 3 4)))
(length (list x x))
- 2
(count-leaves x)
- 4
;---------------------------------------------
(define (count-leaves x)
(cond ((null? x) 0)
((not (pair? x)) 1)
(else (+ (count-leaves (car x))
(count-leaves (cdr x))))))
나무 매핑
• Map과 재귀 처리 방식을 한데 엮어 나무꼴 데이터를 효과적으로다룰 수 있음.
• scale-tree 프로시져 – 곱할 수와나무를 인자로 받아, 모든 나뭇잎에 곱수가 곱해진 나무를 반환.
• 다른 방법은 나무를 부분 나무의차례열로 보고, map을 사용.
(define (scale-tree tree factor)(cond ((null? tree) null)
((not (pair? tree)) (* tree factor))(else (cons (scale-tree (car tree) factor)
(scale-tree (cdr tree) factor)))))(scale-tree (list 1 (list 2 (list 3 4) 5) (list 6 7)) 10)
-------------------------------------------------------
(define tempTree (list 1 (list 2 (list 3 4) 5) (list 6 7)))
(define (scale-tree tree factor)(map (lambda (sub-tree1)
(if (pair? sub-tree1)(scale-tree sub-tree1 factor)(* sub-tree1 factor)))
tree))
(scale-tree tempTree 10);(10 (20 (30 40) 50) (60 70))
2.2.3 공통 인터페이스로써 차례열의 쓰임새
• 나무꼴 데이터를 인자로
받아서 잎사귀 가운데 홀수인
것만 제곱한 다음에 이것을 모두
더한 값을 반환
------------------------------------
• N보다 작거나 N과 같은
정수 K에 대하여 피보나치 수열
Fib(k) 값을 구한 후 짝수만
모아서 리스트로 묶어내는
프로시져
(define (square x) (* x x))(define (sum-odd-squares tree)(cond ((null? tree) 0)
((not (pair? tree))(if (odd? tree) (square tree) 0))(else (+ (sum-odd-squares (car tree))
(sum-odd-squares (cdr tree))))))
----------------------------------------------------
(define (even-fibs n)(define (next k)(if (> k n)
null(let ((f (fib (k))))(if (even? f)
(cons f (next (+ k 1)))(next (+ k 1))))))
(next 0))
• 처리 과정이 비슷함.
• 그러나 두 프로시져는 신호가 흘러가는 구조를 갖추지 못하였음.
• 따라서 두 프로시저 정의에서는 신호 흐름 방식의 단계별 처리 과정이 확실히 대응하는 부분을 찾기 힘듬.
차례열 연산
• 계산 단계에서 다음 계산 단계로 흘러가는신호를 리스트로 나타내면, 모든 단계별처리 과정을 리스트 연산이 가능함.
• 모듈 방식으로 독립된 부품을 짜 맞추듯이프로그램을 설계
Map
• (map square (list 1 2 3 4 5))
• (1 4 9 16 25)
Filter
(define (filter predicate sequence)(cond ((null? sequence) nil)
((predicate (car sequence))(cons (car sequence)
(filter predicate (cdr sequence))))(else (filter predicate (cdr sequence)))))
(filter odd? (list 1 2 3 4 5))
(1 3 5)
Accumulate
(define (accumulate op initial sequence)(if (null? sequence)
initial(op (car sequence)
(accumulate op initial (cdr sequence)))))
(accumulate + 0 (list 1 2 3 4 5))15
(accumulate * 1 (list 1 2 3 4 5))120
Enumerate
(define (enumerate-tree tree)
(cond ((null? tree) nil)
((not (pair? tree)) (list tree))
(else (append (enumerate-tree (car tree))
(enumerate-tree (cdr tree))))))
(enumerate-tree (list 1 (list 2 (list 3 4)) 5))
(1 2 3 4 5)
(define (enumerate-interval low high)
(if (> low high)
nil
(cons low (enumerate-interval (+ low 1) high))))
(enumerate-interval 2 7)
(2 3 4 5 6 7)
모듈 조합
(define (sum-odd-squares tree)
(accumulate +
0
(map square
(filter odd?
(enumerate-tree tree)))))
----------------------------------------------------------------------------
(define (even-fibs n)
(accumulate cons
nil
(filter even?
(map fib
(enumerate-interval 0 n)))))
겹친 매핑
• 차례열 패러다임의 쓰임새를 넓혀서, 겹친 루프를 써서 나타낼 수있는 계산 문제를 표현
• 양의 정수 n, I, j가 있을때, 1 <= j < I <= n 만족하고 I + j 의 값이소수가 되는 i와 j의 모든 순서 쌍을 구하는 문제
• N = 6 일 경우.
• Enumerate– n보다 작거나 같은 양의
정수로 이루어진 모든 순서쌍을 차례열로 묶어냄
• Filter– 그 가운데 그 합이 소수인
쌍들만 거르개로 고름.
• Map– 골라낸 쌍(I,j)에 대해 트리
플(I,j, i+j)를 만듬.
• Accumulate
(accumulate append
nil
(map (lambda (i)
(map (lambda ( j) (list i j))
(enumerate-interval 1 (- i 1))))
(enumerate-interval 1 n)))
(define (flatmap proc seq)
(accumulate append nil (map proc seq)))
----------------------------------------------------------
(define (prime-sum? pair)
(prime? (+ (car pair) (cadr pair))))
----------------------------------------------------------
(define (make-pair-sum pair)
(list (car pair) (cadr pair)
(+ (car pair) (cadr pair))))
(define (prime-sum-pairs n)
(map make-pair-sum
(filter prime-sum?
(flatmap
(lambda (i)
(map (lambda ( j) (list i j))
(enumerate-interval 1 (- i 1))))
(enumerate-interval 1 n)))))
(define (enumerate-interval low high)(if (> low high)
null(cons low (enumerate-interval (+ low 1) high))));------------------------------------------------
(define (accumulate append null)((map (lambda (i)
(map (lambda (j) (list i j))(enumerate-interval 1 (- i 1))))
(enumerate-interval 1 n))))
(define (flatmap proc seq)(accumulate append null (map proc seq)))
;----------------------------------------------------------(define (square x) (* x x))
(define (find-divisor n test-divisor)(cond ((> (square test-divisor) n) n)
((divides? test-divisor n) test-divisor)(else (find-divisor n (+ test-divisor 1)))))
(define (divides? a b) (= (remainder b a) 0))
(define (smallest-divisor n) (find-divisor n 2))
(define (prime? n)(= n (smallest-divisor n)))
(define (prime-sum? pair)(prime? (+ (car pair) (cadr pair))))
;----------------------------------------------------------(define (make-pair-sum pair)
(list (car pair) (cadr pair) (+ (car pair) (cadr pair))))
;----------------------------------------------------------
(define (prime-sum-pairs n)(map make-pair-sum
(filter prime-sum? (flatmap (lambda (i)
(map (lambda (j) (list i j))
(enumerate-interval 1 (- i 1))))(enumerate-interval 1 n)))))
2.2.4 그림 언어
• 데이터 요약과 닫힘 성질, 아울러 차수 높은
프로시저가 프로그램 설계에 미치는 힘을
살펴보기 위해 그림을 그리는데 쓰는 간단한
그림 언어를 만들어 보자.
• 그림언어는 페인터라는 한가지 요소만 갖춤.
• Wave 라는 기본 페인터를 써서 그린 그림.
• Rogers 페인터 사용.
(define wave2 (beside wave (flip-vert wave)))
(define wave4 (below wave2 wave2))
(define (flipped-pairs painter)(let ((painter2 (beside painter (flip-vert painter))))(below painter2 painter2)))
(define wave4 (flipped-pairs wave))
(define (right-split painter n)(if (= n 0)
painter(let ((smaller (right-split painter (- n 1))))
(beside painter (below smaller smaller)))))
-------------------------------------------------(define (corner-split painter n)
(if (= n 0)painter(let ((up (up-split painter (- n 1)))
(right (right-split painter (- n 1))))(let ((top-left (beside up up))
(bottom-right (below right right))(corner (corner-split painter (- n
1))))(beside (below painter top-left)
(below bottom-right corner))))))
그림틀
Origin(Frame) + x Edge1(Frame) + y Edge2(Frame)
(define (frame-coord-map frame)
(lambda (v)
(add-vect
(origin-frame frame)
(add-vect (scale-vect (xcor-vect v) (edge1-frame frame))
(scale-vect (ycor-vect v) (edge2-frame
frame))))))
((frame-coord-map a-frame) (make-vect 0 0))
(origin-frame a-frame)
페인터
• 페인터 프로시져는 그림틀을 인자로 받아서, 그 틀에 맞춘 그림을 그림.
(define (segments->painter segment-list)
(lambda (frame)
(for-each
(lambda (segment)
(draw-line
((frame-coord-map frame) (start-segment segment))
((frame-coord-map frame) (end-segment segment))))
segment-list)))
단단하게 설계할 때 쓰는 언어
• 다층 설계 – 단계별로 여러 언어를 쌓아올려 복잡한 시스템을 층층이 짜맞추어 가는방법
• 다층 설계 방식은 프로그램을 “튼튼하게” 짜는데 큰 도움을 줌.
Referance
• 컴퓨터 프로그램의 구조와 해석 2/E – 헤럴드에빌슨, 제럴드 제이 서스먼, 줄리 서스먼 지음. 인사이트.