레슨 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)

FIRSTREST

리스트가 (첫번째와 나머지) 두 부분으로 만들어졌다고 가정한다면, 여러분은 firstrest 두 연산자를 이용하여 리스트의 개별 원소들을 얻을 수 있습니다:

(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

firstrest 함수를 이리저리 엮는 것은 분명 지루한 작업일 것입니다. 또, 이러한 접근 법은 프로그램에서 특정 요소만을 선택하고자 할때나 혹은 리스트의 길이가 무한일때 잘 먹히지 않을 것입니다. 4장[p 84] 에서 재귀 함수를 정의할때 이러한 문제를 어떻게 해결하는지 살펴볼 것입니다. 나중에 13장에서 [p 150], 리스프가 제공하는 리스트 혹은 시퀀스 속 요소를 선택할 수 있는 함수들을 살펴 볼 것입니다

firstrest는 꽤 최근 동안 활약해온 carcdr 함수의 이름을 바꾸어 리스프에 추가된 것입니다. 초기 리스프 구현체 중 하나에서 carcdr의 이름이 유례됬고, 이 이름에 기반한 구현체가 이미 오래전에 바뀌었음에도 불구하고 수십년간 고수되었습니다

짚고 넘어가기

  • cons
  • list
  • first
  • rest
  • car
  • cdr
  • nil , ( )
  • T