레슨 04. 조립과 분해
CONS
cons
는 리스트의 가장 기본적인 구성 요소입니다. 이는 함수므로 이의 인자들을 평가합니다. 리스트를 만들때에는 cons
의 두번째 인자로 리스트
혹은 NIL
이 들어올것입니다.
(cons 1 nil)
;;=> (1)
(cons 2 (cons 1 nil))
;;=> (2 1)
(cons 3 (cons 2 (cons 1 nil)))
;;=> (3 2 1)
cons
는 새로운 항목을 리스트의 시작 부분에 추가합니다. 비어있는 리스트 ( )
는 NIL
과 동일하며,
( ) == NIL
따라서 이렇게 작성할 수 있습니다:
(cons 1 ())
;;=> (1)
(cons 2 (cons 1 ()))
;;=> (2 1)
(cons 3 (cons 2 (cons 1 ())))
;;=> (3 2 1)
혼란스럽게 느껴진다면, 맞습니다, NIL
에는 뭔가 특별한 능력이 있습니다. NIL
은 키워드는 아니지만 자기 자신을 상수 값으로 가지는 리스프의 두개의 심볼 중 하나입니다. 또 다른 심볼은 T
입니다.
( ) == NIL
이란 것과 NIL
이 스스로 평가된다는 것을 종합해보면, 이는 (quote ())
를 ()
로 쓸 수 있다는 것을 의미합니다. 그렇지 않았다면, 리스프는 빈 리스트를 처리하기 위해 평가 규칙에 예외를 추가해야 했을 것입니다.
LIST
아마 여러분이 눈치챗다면, 중첩된 cons
폼으로도 리스트가 만들어질 수 있다는 것은 조금 지루할 수 도 있습니다. list
폼은 좀더 명료한 방법으로 동일한 일을 수행합니다:
(list 1 2 3)
;;=> (1 2 3)
list
는 여러 인자를 취할 수 있습니다. list
는 함수이기에, 이는 인자를 평가합니다:
(list 1 2 :hello "there" 3)
;;=> (1 2 :HELLO "there" 3)
(let ((a :this)
(b :and)
(c :that))
(list a 1 b c 2))
;;=> (:THIS 1 :AND :THAT 2)
FIRST와 REST
리스트가 (첫번째와 나머지) 두 부분으로 만들어졌다고 가정한다면, 여러분은 first
와 rest
두 연산자를 이용하여 리스트의 개별 원소들을 얻을 수 있습니다:
(setq my-list (quote (1 2 3 4 5)))
;;=> (1 2 3 4 5)
(first my-list)
;;=> 1
(rest my-list)
;;=> (2 3 4 5)
(first (rest my-list))
;;=> 2
(rest (rest my-list))
;;=> (3 4 5)
(first (rest (rest my-list)))
;;=> 3
(rest (rest (rest my-list)))
;;=> (4 5)
(first (rest (rest (rest my-list))))
;;=> 4
first
와 rest
함수를 이리저리 엮는 것은 분명 지루한 작업일 것입니다. 또, 이러한 접근 법은 프로그램에서 특정 요소만을 선택하고자 할때나 혹은 리스트의 길이가 무한일때 잘 먹히지 않을 것입니다. 4장[p 84] 에서 재귀 함수를 정의할때 이러한 문제를 어떻게 해결하는지 살펴볼 것입니다. 나중에 13장에서 [p 150], 리스프가 제공하는 리스트 혹은 시퀀스 속 요소를 선택할 수 있는 함수들을 살펴 볼 것입니다
first
와 rest
는 꽤 최근 동안 활약해온 car
와 cdr
함수의 이름을 바꾸어 리스프에 추가된 것입니다. 초기 리스프 구현체 중 하나에서 car
와 cdr
의 이름이 유례됬고, 이 이름에 기반한 구현체가 이미 오래전에 바뀌었음에도 불구하고 수십년간 고수되었습니다
짚고 넘어가기
cons
list
first
rest
car
cdr
nil
,( )
T