(type-of 123)
;;=> FIXNUM
@@ -490,10 +485,10 @@
-해쉬 테이블은 값과 유니크 키(유일 키, unique key)와 관련있습니다. 프로퍼티 리스트완 다르게, 해쉬 테이블은 엄청나게 많은 키/값쌍에 매우 적합하지만, 작은 연관 set에 대해 엄청난 오버헤드(overhead)를 감수해야합니다.
+TYPE-OF
는 인수의 타입을 나타내는 심볼 혹은 리스트를 반환합니다. 게다가 이 정보를 이용하여 인자의 타입에 기반한 프로그램의 동작 방식을 조정 할 수 있습니다. TYPECASE
함수는 타입에 대한 질의(inquiry)와 COND-와 같은 분기(dispatch)를 결합한 것입니다.
+CLOS(14장 [p 157] 참조)의 제네릭 함수의 도입으로, TYPE-OF
는 예전만큼 중요하지는 않습니다.
+
+해쉬 테이블은 유니크 키(유일 키, unique key)와 값의 연결로 이루어져 있습니다. 프로퍼티 리스트완 다르게, 해쉬 테이블은 수 많은 키/값 쌍에 적합하며, 적은 수의 연결 집합에 대해서는 과도한 오버헤드(overhead)가 발생합니다.
(setq ht1 (make-hash-table))
;;=> #<HASH-TABLE>
@@ -515,23 +510,22 @@
리스트를 키로 사용하고자 한다면, 이렇게 해쉬테이블을 만듭니다:
(make-hash-table :test #'equal)
-키를 없애고자한다면, (REMHASH key hash-table)
폼을 이용합니다. 그리고 키에 해당하는 값을 바꾸고자한다면, 키/값쌍을 추가했던것처럼 GETHASH
와 SETF
를 이용합니다.
-
-프로그램을 작성하는것에 관한 어려운 일중 하나는 프로그램의 이름을 짓는 부분입니다. 한편으론, 기억하기 쉽고 명명된 object의 규칙과 목적을 환기evocative시켜주는 이름을 이용하기 원할 것입니다. 다른 한편으론, 언젠가 여러분의 프로그램과 작업하게될, 다른 프로그램의 어딘가에 이미 사용한 (혹은 사용될 것 같은)이름을 이용하지 않길 원할 것입니다.
-이름 충돌(naming conflict)을 피하는 법 중 하나는 프로그램에 있는 모든 이름에 누구도 사용하지 않는 고유 접두사(prefix)를 붙이는 것입니다. 여러분은 이것이 라이브러리에 매번 필요하다고 생각할 것입니다 - 보통 하나에서 세개정도 문자의 접두사가 있다. 불행히도, 여전히 두 소프트웨어 개발자가 동일한 prefix를 선택하기 위한 많은 관문이 남아있습니다; 유달히 몇몇 prefix들이 다른 것보다 더욱 evocative하기 때문입니다. 여러분이 생산한 모든 소프트웨어를 제어하고자 한다면, 모든 prefix를 선택하고 문제를 피할 수 있습니다. prefix naming scheme을 이용하는 third-parfy 소프트웨어를 사고자 한다면, 제조사에 의해 선택된 이름에 관련된 일을 해야할 것이며, 두 제조사가 우연히 동일한 prefix를 사용하지 않기를 희망할 것입니다.
-
-이름 충돌을 피하는 또 하나의 방법은 qualified name을 이용하는 것입니다. 이를 하기 위해선, 언어가 프로그래머에 의해 정의되고 제어되는 분리된 이름공간의 지원을 제공해야 합니다. 어떻게 이것이 동작하는지 이해하기 위해, 프로그램을 위해 여러분이 만든 모든 이름이 여러분의 이름이 타이틀 상단에 쓰여진 한장에 종이에 작성되었다고 상상해보시기 바랍니다. 이름이 이용하기에 안전한지 확인하기 위해선, 여러분이 이 페이지에 작성한 이름 목록을 확인하기만 하면 됩니다. 누군가의 소프트웨어가 여러분의 프로그램의 서비스를 필요로 할때, 그것은 여러분의 qualifier와 name을 이용하여 여러분의 이름을 참조합니다. 다른이의 소프트웨어는 다른 qualifier를 지니고 그들의 qualifier는 그들만의 name을 암시하기에(이것을 작성할 필요는 없습니다), 이름 충돌을 피할 방도가 없습니다.
-여러분은 qualifier는 이름에 prefix를 붙이기 위한 복잡한 방법에 지나지 않는다고 생각할 것입니다. 그러나, 거기엔 미묘하며 중대한 차이점이 있습니다. prefix는 이름의 일부입니다; 이는 한번 작성되면 바뀔 수 없습니다. qualifier는 이가 qualify한 이름에서 분리되었으며, 정확히 한 장소에서 "작성"되었습니다. 더욱이, 여러분은 이름들이 쓰여진 "종이"를 가리켜 이를 "그러한 이름들"이라 말할 수 있습니다. 다른 프로그래머와 동일한 qualifier를 선택하게 된다면, 여러분은 여전히 여러분이 선택한 qualifier에 의해 "그 이름"을 참조할 수 있습니다 - 다른 말로 하자면, 여러분이 사용할 소프트웨어가 출고된 후에도, 여러분은 qualifier를 바꿀 수 있습니다.
+키를 없애고자 한다면 (REMHASH key hash-table)
폼을 이용합니다. 그리고 키에 해당하는 값을 바꾸고자한다면, 키/값 쌍을 추가했던 것처럼 GETHASH
와 SETF
를 이용합니다.
+
+프로그램을 작성시 어려운 점 중 하나는 이름 짓는 것입니다. 한편으론, 기억하기 쉽고 해당 객체의 역할이나 목적을 떠올릴 수 있는 이름을 사용하기 원할 것입니다. 그리고 다른 한편으론, 다른 프로그램의 어딘가에 이미 사용한 (혹은 사용될 것 같은)이름을 여러분의 프로그램에서 사용하고 싶진 않을 것입니다.
+이름 충돌(naming conflict)을 피하는 법 중 하나는 프로그램에 있는 모든 이름에 누구도 사용하지 않는 고유 접두사(prefix)를 붙이는 것입니다. 라이브러리에서 이러한 방식을 자주 볼 수 있습니다 - 보통 1~3자로 접두사를 붙입니다. 불행히도, 여전히 두 소프트웨어 개발자가 동일한 접두사를 선택할 수 있는 관문이 남아있습니다; 일부 접두사가 다른 접두사들보다 더 매력적인 경우. 모든 소프트웨어를 제어할 수 있다면, 모든 접두사를 선택하여 문제를 피할 수 있습니다. 타사 소프트웨어를 구입하는 경우, 제조사에 의해 선택된 이름을 사용해야만 하며, 동일한 접두사를 사용하지 않기를 바래야만 할 것입니다.
+
+이름 충돌을 피하는 또 하나의 방법은 한정된 이름(qualified name)을 사용하는 것입니다. 이를 위해, 언어는 프로그래머에 의해 정의되고 제어되는 분리된 이름공간을 제공해야 합니다. 어떻게 이것이 동작하는지 이해하기 위해, 프로그램에 쓰이는 모든 이름이 한장에 종이 상단의 타이틀 부분에 작성되었다고 상상해보시기 바랍니다. 이름을 사용해도 안전한지 확인하려면, 이 페이지에 작성한 이름 목록을 확인하기만 하면 됩니다. 누군가의 소프트웨어가 여러분의 프로그램의 서비스를 필요로 할 때, 그 누군가는 여러분들이 한정한 이름을 참조할 것입니다. 다른이의 소프트웨어는 그들만의 규칙이 이름을 한정할 것이며 있을 것이며, 이름 충돌이 발생할 가능성이 없습니다.
+한정자는 이름에 접두사를 추가하는 복잡한 방법에 지나지 않는다고 생각할 수 있습니다. 그러나, 거기엔 미묘하며 중대한 차이점이 있습니다. 접두사는 이름의 일부이므로 한 번 쓰면 변경할 수 없습니다. 한정자는 한정하는 이름과 분리되어 있으며 정확히 한 곳에 "기록"되었습니다. 더욱이, 이름이 적혀 있는 "종이"를 가리키며 "이러한 이름들"이라고 지칭할 수 있습니다. 다른 프로그래머와 동일한 한정자를 선택한 경우에도, 자신이 선택한 한정자로 "그 이름들"을 지칭할 수 있습니다 - 다른 말로 하자면, 여러분이 사용할 소프트웨어가 출고된 후에도, 한정자를 변경할 수 있습니다.
-위 예제에는, 파일 LIB1과 LIB2에서 가져온 두 라이브러리가 있습니다. 두 라이브러리 설계자는, 리스프에서 패키지 이름으로 알려진, 이름 공간namespace을 위해 UTIL이란 이름을 사용하였습니다. 각 라이브러리는 client에게 보여지는 이름들을 나열했습니다. 두 라이브러리를 이용하는 프로그래머는 MY-PACKAGE
란 패키지 이름에서 코드를 작성합니다. 각 라이브러리를 로드한후, 프로그래머는 이의 package의 이름을 다른 이름으로 바꿉니다. 그러면, UTIL-1:INITIALIZE
와 UTIL-2:INITIALIZE
의 호출에서 봤던 것처럼, 라이브러리에 있는 이름들은 이름이 바뀐 qualifier를 이용하여 참조됩니다. 프로그래머는 여전히 qualify 폼이 아닌 INITIALIZE 이름은 이용할 수 있다는 것을 주목하시기 바랍니다 - 이는 MY-PACKAGE:INITIALIZE
와 동일합니다.
-리스프는 package facility라 알려진 함수와 매크로를 통해 이러한 기능을 제공합니다. DEFPACKAGE
매크로는 간편하게 대부분의 패키지 연산을 제공하는데 반해, IN-PACKAGE
매크로는 현재 패키지를 설정합니다:
+위 예로 두 라이브러리 LIB1
와 LIB2
가 있습니다. 두 라이브러리를 설계한 사람은 리스프에서 패키지 이름으로 알려진 이름 공간(namespace)을 UTIL
이라 이름 붙였습니다. 각 라이브러리는 클라이언트에 노출될 이름들이 나열되어 있습니다. 두 라이브러리를 이용하는 프로그래머는 MY-PACKAGE
란 패키지 이름에서 코드를 작성했습니다. 각 라이브러리를 로드한 후, 이름들을 구분할 수 있도록 프로그래머는 이의 패키지 이름을 바꾸었습니다. 그러면, UTIL-1:INITIALIZE
와 UTIL-2:INITIALIZE
의 호출에서 봤던 것처럼, 라이브러리에 있는 이름들은 이름이 바뀐 한정자를 활용하여 참조됩니다. 프로그래머는 한정자가 없는 INITIALIZE
란 이름을 여전히 사용 할 수 있다는 점을 주목하시기 바랍니다 - 이는 MY-PACKAGE:INITIALIZE
와 동일합니다.
;;;; file: util1.lisp
(defpackage util1
(:export init func1 func2)
@@ -567,10 +561,10 @@
read
diff --git a/ch03/lesson_11.html b/ch03/lesson_11.html
index a2a0dbe..7648cfb 100644
--- a/ch03/lesson_11.html
+++ b/ch03/lesson_11.html
@@ -183,13 +183,13 @@
-레슨 10에서 봤던것처럼, read
는 문자를 리스프 데이터로 변환시킵니다. 이제까지, 우리들은 리스프 데이터 여러 출력 값들을 보았습니다:
+레슨 10에서 봤던것처럼, read
는 문자를 리스프 데이터로 변환시킵니다. 이제까지, 우리들은 리스프 데이터 여러 출력 값들을 살펴 보았습니다:
- 심볼과 숫자
- 문자, 문자열, 리스트, 배열, 백터, 구조체
- 해쉬태이블
-리스프 리더는 문자의 분류법(classifications)를 따라 일을 수행합니다. 표준 분류법은 아래에 나와있습니다. 레슨 12에서 보게될 것처럼, 여러분은 필요에 따라 이러한 분류법을 바꿀 수도 있습니다.
+리스프 리더(reader)는 문자 분류법(classifications)를 따라 이와 같은 일을 수행합니다. 표준 분류법은 아래에 나와있습니다. 레슨 12에서 보게될 것처럼, 여러분은 필요에 따라 이러한 분류법을 바꿀 수도 있습니다.
표준 구성 문자(Standard Constituent Characters)
-------------------------------
a b c d e f g h i j k l m n o p q r s t u v w x y z
@@ -201,7 +201,7 @@ 문자 | 설명 |
" | 문자열을 읽는다. |
-’ | 폼을 읽는다. |
+' | 폼을 읽는다. |
( | 리스트를 읽는다. |
; | 새로운 라인을 만나기 전까지 모든 것을 무시한다. |
# | 다음 나오는 문자에 기반하여 무엇인지 결정한다. |
-마지막으로, 몇몇 리스프 데이터는 읽을 수 없습니다. 예를들어, 해쉬테이블이 출력되어 보여지는 것은 #<HASH-TABLE>
와 같이 보여집니다. #<
문자로 시작하는 어떠한 것을 읽으려할때 READ는 에러를 낼 것입니다.
-
-print
함수는 리스프 오브젝트를, READ가 이를 재구성해야만 하는, 문자열(sequence of characters)로 바꿉니다:
-(print ’abc)
-= \newline ABC \space
+마지막으로, 몇몇 리스프 데이터는 읽을 수 없습니다. 예를들어, 해쉬테이블의 출력결과는 #<HASH-TABLE>
와 같습니다. #<
문자로 시작하는 어떠한 것을 읽으려할때 READ
는 에러를 뱉을 것입니다.
+
+print
함수는 리스프 오브젝트를 문자 시퀀스(sequence of characters)로 바꿉니다. 이 문자 시퀀스는 READ
에서 재구성하는 데 필요로 하는 것입니다:
+(print 'abc)
+;;>> ⏎
+;;>> ABC⌴
;;=> ABC
(print (list 1 2 3))
-= \newline (1 2 3) \space
+;;>> ⏎
+;;>> (1 2 3)⌴
;;=> (1 2 3)
(print "A String")
-= \newline "A string" \space
+;;>> ⏎
+;;>> "A string"⌴
;;=> "A string"
(print 387.9532)
-= \newline 387.9532 \space
+;;>> ⏎
+;;>> 387.9532⌴
;;=> 387.9532
(print (make-hash-table))
-= \newline #<HASH-TABLE> \space
+;;>> ⏎
+;;>> #<HASH-TABLE>⌴
;;=> #<HASH-TABLE>
-PRINT
는 개행문자(\newline
)로 시작하고 띄어쓰기(\space
)으로 끝나는 결과물을 출력합니다. 개행과 띄어쓰기 모두 공백(whitespace)으로 취급되므로, (escape가 아닌)리스프 오브젝트가 출력된 표현의 일부가 될수 없기에, 이는 PRINT 결과물이 다른 결과물과는 다르다는 것을 보증합니다.
-PRINT
의 다른 변종들은 다양한 쓰임세를 지녔습니다. PRIN1
은 PRINT
처럼 행동하지만, whitespace로 감싸지 않습니다. 예를들어 연이은 조각들로부터 이름을 만들고자 할때, 이는 유용할 것입니다. PRINC는 PRIN1처럼 행동하지만, 보여는것 보단 READ를 위한 결과물을 생성합니다; 예를들어, PRINC는 문자열을 둘러싸는 쌍따옴표(quotes)를 생략하며, escape character를 출력하지 않습니다.
-(print ’a\ bc)
-;;>> \newline |A BC| \space
+PRINT
는 개행(⏎)으로 시작하고 띄어쓰기(⌴)으로 끝나는 결과물을 출력합니다. 이는 PRINT
출력이 다른 출력들과 구별되도록 만들어 줍니다. 개행과 띄어쓰기 모두 공백(whitespace)으로 취급되며, 이는 (이스케이프되지 않는 한) 리스프 오브젝트를 출력하는데 하는데 포함될 수 없습니다.
+PRINT
와 비슷한 다양한 쓰임세를 지닌 다른 변종들이 있습니다. PRIN1
은 PRINT
와 비슷하게 행동하지만, 공백으론 감싸지 않습니다. 예를들어 여러 조각들로 이름을 만들고자 할때 유용하게 쓰일 것입니다. PRINC
는 PRIN1
처럼 행동하지만 READ
를 위하기 보단, 유저에게 보여지는것을 위한 결과물을 생성합니다; 예를들어, PRINC
는 문자열을 둘러싸는 쌍따옴표를 생략하며, 이스케이프 문자 역시 출력하지 않습니다.
+(print 'a\ bc)
+;;>> ⏎
+;;>> |A BC|⌴
;;=> |A BC|
-(prin1 ’a\ bc)
+(prin1 'a\ bc)
;;>> |A BC|
;;=> |A BC|
-(princ ’|A BC|)
-;;>> \newline A BC \space
+(princ '|A BC|)
+;;>> ⏎
+;;>> A BC⌴
;;=> |A BC|
-보통, READ
는 키보드를 읽고 PRINT
는 화면에 출력합니다. 이러한 함수 모두 옵셔널(optional) 인자를 취합니다; 인자는 READ
를 위한 입력 스트림과 PRINT
를 위한 출력 스트림을 지정합니다. 스트림(stream)은 무엇일까요? 스트림은 데이터의 근원(source)이자 밑바닥(sink)이며, 보통 (그치만 절대적이지는 않은) 문자(characters)입니다. 이제부터, 저희는 tex file이 어떻게 문자 stream의 source나 sink가 될 수 있는지를 살펴볼 것입니다. 19장 [p 183]에서 저희는 몇몇 다른 가능성들을 살펴볼 것입니다.
-여러분은, 파일 이름을 인자로 취하고 스트림의 방향(입력과 출력)을 결정하는 키워드 인자를 취하는, OPEN
함수를 이용하여 스트림을 파일로 붙일 수 있습니다. 스트림에 관한 작업을 종료하고 관련된 파일을 닫기 위해선, CLOSE
함수를 사용합니다.
+보통, READ
는 키보드를 읽고 PRINT
는 화면에 출력합니다. 이러한 함수 모두 옵셔널(optional) 인수를 취합니다; 인수로 READ
를 위한 입력 스트림과 PRINT
를 위한 출력 스트림을 지정합니다. 스트림(stream)은 무엇일까요? 스트림은 데이터의 소스(source)이자 밑바닥(sink)이며, 보통은 (그치만 절대적이지는 않은) 문자들(characters)입니다. 이제부터, 저희는 텍스트 파일이 어떻게 문자 스트림의 소스가 될 수 있는지를 살펴볼 것입니다. 19장 [p 183]에서 저희는 몇몇 다른 가능성들을 살펴볼 것입니다.
+파일 이름을 인수로 취하고 스트림의 방향(입력과 출력)을 결정하는 키워드 인수를 취하는, OPEN
함수를 이용하여 스트림을 파일로 연결 시킬 수 있습니다. 스트림에 대한 작업을 끝내고 연결된 파일을 닫기 위해선 CLOSE
함수를 사용합니다.
(setq out-stream (open "my-temp-file" :direction :output))
;;=> #<OUTPUT-STREAM "my-temp-file">
@@ -292,30 +299,30 @@
-이 예제에서, 저희는 my-temp-file
에 대한 출력 스트림을 만들고, 심볼 ABC
를 그 스트림에 출력하였습니다. 인자는 평상시처럼 반환하지만 출력을 하지 않는 다는 것을 주목하시기 바랍니다 - 대신, 출력된 결과는 파일로 갑니다.
-다음으로, 출력 스트림을 닫고 동일한 파일에 입력 스트림을 열었습니다. 그런 다음 저희가 파일에 출력한 심볼을 읽어온 다음, 입력 스트림을 닫음으로써 끝을 맺습니다.
-
-리스프는 또한, 이러한 옵션을 제어하는 키워드 인자를 이용하여, 여러분에게 출력에 대해 더욱 세부적인 것에 관한 제어권을 주는 WRITE
함수를 제공합니다:
-Keyword Argument | Default Value | Action |
-:stream | t | set output stream |
-:escape | *print-escape* | include escape characters |
-:radix | *print-radix* | include radix (base) prefix |
-:base | *print-base* | set number base (rationals) |
-:circle | *print-circle* | print circular structures |
-:pretty | *print-pretty* | add whitespace for readability |
-:level | *print-level* | limit nesting depth |
-:length | *print-length* | limit items per nesting level |
-:case | *print-case* | :upper, :lower, or :mixed |
-:gensym | *print-gensym* | prefix uninterned symbols |
-:array | *print-array* | print arrays readably |
-:readably | *print-readably* | force printing to be readable |
-:right-margin | *print-right-margin* | controls pretty-printing |
-:miser-width | *print-miser-width* | " |
-:lines | *print-lines* | " |
-:pprint-dispatch | *print-pprint-dispatch* | " |
+이 예제에서, 저희는 my-temp-file
에 대한 출력 스트림을 만들었으며, 심볼 ABC
를 그 스트림에 출력하였습니다. 여느때와 같이 인자를 반환하지만, 출력은 하지않았다는 점을 주목해주시길 바랍니다 - 출력된 결과는 파일로 갔습니다.
+다음으로, 출력 스트림을 닫고 동일한 파일로 입력 스트림을 열었습니다. 그런 다음 저희가 파일에 출력한 심볼을 읽어온 다음, 입력 스트림을 닫음으로써 끝을 맺었습니다.
+
+리스프는 또한 이러한 옵션을 제어하는 키워드 인자를 사용하여, 출력을 더욱 세부적으로 제어할 수 있는 WRITE
라는 함수를 제공합니다:
+키워드 인자 | 기본 값 | 행동 |
+:stream | t | 출력 스트림 설정 |
+:escape | *print-escape* | 이스케이프 문자 포함 |
+:radix | *print-radix* | 진법(radix (base)) 프리픽스 |
+:base | *print-base* | 숫자가 몇 진법을 사용할지 설정 |
+:circle | *print-circle* | 순환(circular) 구조물 출력 |
+:pretty | *print-pretty* | 가독성을 위한 공백 추가 |
+:level | *print-level* | 중첩 단계 한계 |
+:length | *print-length* | 중첩 단계당 아이템의 한계 |
+:case | *print-case* | :upper, :lower, 혹은 :mixed |
+:gensym | *print-gensym* | uninterned 심볼의 접두사 |
+:array | *print-array* | 가독성 있게 배열 출력 |
+:readably | *print-readably* | 강제로 가독성있게 |
+:right-margin | *print-right-margin* | 가독성있게 출력하는 옵션 |
+:miser-width | *print-miser-width* | '' |
+:lines | *print-lines* | '' |
+:pprint-dispatch | *print-pprint-dispatch* | '' |
-우연하게도, 위에 키워드 인자의 기본값으로 나온 변수들은 또한 PRINT
의 연산을 제어합니다. 여러분은, PRIN1
을 감싸는 LET
폼에서 이러한 변수들을 바인딩 함으로써, non-default 키워드 인자로 WRITE
의 효과를 얻을 수 있습니다:
+우연하게도, 위에 키워드 인자의 기본값으로 나온 변수들은 또한 PRINT
의 연산을 제어합니다. 여러분은, 키워드 인자를 쓰지 않고 LET
폼에서 이러한 변수에 바인딩 하고 PRIN1
를 감싸면 WRITE
과 동일한 효과를 얻을 수 있습니다:
(write foo
:pretty t
:right-margin 60
@@ -327,28 +334,29 @@