레슨 05. 네이밍과 아이덴티티(Naming and Identity)

심볼은 단지 이름

심볼은 단지 이름입니다. 심볼은 심볼 그 자체 입니다. 이는 리스프에서 특정 종류의 프로그램을 작성하는 것을 쉽게 만들어 줍니다. 예를들어, 프로그램으로 가족 관계를 나타내고자 한다면, 이와 같은 관계를 나타내는 데이터베이스를 만들 수 있습니다:

(father John Barry)
(son John Harold)
(father John Susan)
(mother Edith Barry)
(mother Edith Susan)

...

각각의 관계를 리스트로 표현했습니다. 예로 (father John Barry)JohnBarry의 아버지라는 것을 의미합니다. 데이터베이스 속 리스트의 모든 요소는 심볼입니다. 예를들어 Harold는 Barry의 할아버지라는 것을 판별하기 위해 리스프 프로그램은 이 데이터베이스안에 있는 심볼들을 비교할 수 있습니다.

심볼이 없는 다른 언어 언어에서 이와 같은 프로그램을 작성하고자 한다면, 가족 구성원의 이름과 관계를 어떻게 표현해야할지 결정해야만 해야하며, 거기에 필요한 모든 동작들을 (읽고, 출력하고, 비교하고, 할당하고, 기타등등) 수행하는 코드를 작성해야만 할 것입니다. 이름을 지정하는 데 사용되는 오브젝트들과는 별개의 데이터 타입인 심볼이기에 이 모든 것들이 리스프에 이미 내장되어 있습니다.

심볼은 항상 유니크하다.

프로그램에서 이름이 같은 심볼은 항상 동일합니다. eq 테스트로 심볼을 비교할 수 있습니다:

(eq 'a 'a)
;;=> T

(eq 'david 'a)
;;=> NIL

(eq 'David 'DAVID)
;;=> T

(setq zzz 'sleeper)
;;=> SLEEPER

(eq zzz 'sleeper)
;;=> T

심볼 이름으로 대문자나 소문자를 사용하는 것은 문제가 되지 않습니다. 내부적으로, 리스프는 심볼 이름에 있는 모든 알파벳 문자들을 보통은 대문자로 바꾸며, 리스프 리더 속에 있는 플레그를 설정하여 이 기본 설정을 제어할 수 있습니다. 레슨 10 [p 65](또한 31장 [p 247]참조)에서 패키지에 대해 배운다면, 스펠은 같지만 동일하지는 않는 심볼 이름을 만들 수 있습니다. 지금 알아야 할것은 : 로 표시된 심볼은 특별하게 취급한다 라는 것입니다.

심볼로 값에 이름 붙일 수 있다.

심볼의 자기 자신을 표현하는 능력 역시 유용하지만, 더욱 일반적으로 사용되는 곳은 값의 이름을 붙일 때입니다. 이는 다른 프로그래밍 언어에서 변수와 함수 이름의 역활을 맡습니다. 리스프 심볼은 값의 이름이나, 함수의 이름을 지을 때 사용됩니다.

리스프에서 특이한 점 중 하나는 심볼은 함수랑 변수의 값을 동시에 지닐 수 있다 라는 점 입니다:

(setq first 'number-one)
;;=> NUMBER-ONE

(first (list 3 2 1))
;;=> 3

first
;;=> NUMBER-ONE

첫번째와 마지막 경우에서의 first변수 이며 , 두번째인 경우는 리스프에 의해 미리 정의된 함수 라는 점을 주의깊게 보시길 바랍니다. 리스프는 심볼이 보이는 곳에 기반하여 이 값이 무엇인지 결정합니다. 평가 규칙에 따라 값을 요청받으면, 리스프는 심볼의 변수 값을 찾습니다. 함수를 요청받으면, 리스프는 심볼의 함수 값을 찾습니다.

심볼은 변수나 함수의 값 외에 다른 값을 가질 수 있습니다. 심볼은 문서, 프로퍼티 리스트(property list) 혹은 출력시 표시되는 값을 가질 수 있습니다. 심볼의 문서는 해당 심볼의 설명을 위한 텍스트입니다. documentation 폼을 사용하거나 심볼의 값을 정의하는 몇몇 폼에서 심볼에 대한 문서도 같이 만들 수 있습니다. 심볼은 다양한 의미를 가질 수 있으므로 함수 및 변수와 같이 여러 가지 의미에 맞는 각각에 대한 문서를 만들 수 있습니다.

프로퍼티 리스트는 엔트리(entry)당 하나의 키를 지닌 자그마한 데이터 베이스와 같습니다. 레슨 10 [p 65]에서 이러한 심볼의 사용법을 살펴볼 것입니다.

출력 이름은 리스프에서 심볼을 출력하기 위해 사용하는 것입니다. 일반적으로 이 이름을 변경하지 않는게 좋습니다; 다른 이름으로 변경한다면, 다른 이름으로 출력된 결과를 리스프가 다시 읽어들일때 원래 심볼값과 다른 의미로 받아들여 혼란을 야기할 것입니다.

값은 하나 이상의 이름을 가질 수 있다

값은 하나 이상의 이름을 가질 수 있습니다. 즉, 하나 이상의 심볼이 동일한 값을 공유할 수 있다는 것입니다. 다른 언어에서 이러한 방식으로 동작하는 것으로는 포인터가 있습니다. 리스프는 프로그래머에게 포인터를 노출하진 않지만, 공유되어 사용되는 오브젝트들이 있습니다. eq 테스트로 오브젝트가 서로 같은지 확인할 수 있습니다. 다음에 나오는 것을 살펴보시기 바랍니다:

(setq L1 (list 'a 'b 'c))
;;=> (A B C)

(setq L2 L1)
;;=> (A B C)

(eq L1 L2)
;;=> T

(setq L3 (list 'a 'b 'c))
;;=> (A B C)

(eq L3 L1)
;;=> NIL

여기서, 동일한 값을 L1L2라 이름지엇기에, L1L2eq합니다. 다시 말하자면 (list 'a 'b 'c) 폼에 의해 생성된 값은 L1L2라는 두개의 이름을 갖습니다. (setq L2 L1) 폼은 "L2의 값이 L1의 값이 되도록 해라" 라고 지시합니다. 값의 복사를 말 하는게 아니라, 값 그 자체를 지칭합니다. 따라서 L1L2는 동일한 값 (처음 L1에 할당된 리스트 (A B C))을 공유합니다.

또한 L3도 리스트 (A B C)를 값으로 갖지만, 이는 L1L2가 공유하는 것과는 다른 새로운 리스트 입니다. 비록 L3의 값이 L1L2의 값과 동일한 것처럼 보이지만, 다른 list 폼에 의해 생성되었기에, 이는 다른 리스트입니다. 따라서, 서로 심볼 A, B, C로 구성된 리스트이지만, 다른 리스트이기에 (eq L3 L1)NIL이 됩니다.

짚고 넘어가기

  • eq