목차
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 부록A
01장. 궂이 왜? 혹은, 반론에 대한 답변
목표: Lisp에 대한 가장 흔한 반론들과 답변 + 최신 구현체 소개 및 본서에서 다룰 내용 미리보기
- 전에 Lisp를 봤는데 이해가 안 갔어요.
- 괄호 속 프로그램 구조가 보이지 않아요(작업 방식이 명확하지 않음).
- 제가 좋아하는 언어보다 훨씬 느려요.
- 다른 사람들은 Lisp로 개발하지 않아요 (사용률 낮음).
- GUI를 사용할 수 없어요.
- 외부 코드 호출이 불가능해요(타 시스템 통합 어려움).
- 실행 중 예측 불가한 지연 발생 (GC 관련).
- 언어 규모가 너무 커서 복잡해요.
- 인공지능 연구용으로만 사용된다고 알려져 있어요.
- 효율적인 개발 도구가 부족해요.
- 메모리 사용량이 과도해요.
- 디스크 공간을 많이 차지해요.
- 좋은 Lisp컴파일러를 찾을 수 없어요.
02장. 이 책은 나에게 적합할까?
목표: 이 책이 독자에게 어떤 도움을 주는지 구체적인 예시와 장별 내용 참조를 통해 설명
03장. 12개의 레슨으로 배우는 필수 리스프
목표: Lisp을 가장 간단한 형태로 설명(초보자를 혼란스럽게 할 수 있는 특수한 경우를 고려하지 않고)
- 레슨 01. 필수 - 신택스
- 리스트는 괄호로 둘러싸여 있다.
- 아톰(atom)은 공백이나 괄호로 구분된다.
- 레슨 02. 필수 - 평가
- 폼(form)은 평가될 수 있다.
- 함수는 인자를 적용할 수 있다.
- 함수는 여러 개의 값을 반환할 수 있다.
- 함수는 일반적으로 인자를 수정하지 않는다.
- 함수 적용 전에 인자가 먼저 평가된다.
- 인자는 왼쪽에서 오른쪽 순서로 평가된다.
- 스페셜 폼(special forms)과 매크로(macros)는 인자의 평가 방식을 바꾼다.
- 레슨 03. 스페셜 폼과 매크로에 대한 예제
- SETQ
- LET
- COND
- QUOTE
- 레슨 04. 조립과 분해
- CONS
- LIST
- FIRST 와 REST
- 레슨 05. 네이밍과 아이덴티티
- 심볼(symbol)은 단순히 이름일 뿐이다.
- 심볼은 항상 고유하다.
- 심볼은 값을 가리킬 수 있다.
- 하나의 값에는 여러 심볼이 연결될 수 있다.
- 레슨 06. 바인딩 vs 할당
- 바인딩(binding)은 값을 저장할 새로운 공간을 만든다.
- 바인딩은 이름을 지닌다.
- 동일한 시점에 한 바인딩이 서로 다른 값을 가질 수 있다.
- 가장 안쪽의 바인딩을 우선시 한다.
- 프로그램은 오직 자신이 생성한 바인딩에만 접근할 수 있다.
- 할당(assignment)은 기존 공간에 새로운 값을 부여한다.
- 레슨 07. 필수 - 함수 정의
- DEFUN 이름이 있는 함수를 정의한다.
- LAMBDA 이름이 없는(익명) 함수를 정의한다.
- 레슨 08. 필수 - 매크로 정의
- DEFMACRO 매크로를 정의한다.
- 매크로는 값이 아닌 폼(form)을 반환한다.
- 레슨 09. 필수 - 다중 값
- 대부분의 표현식은 하나의 값만 생성한다.
- VALUES 표현식은 여러 개(또는 없음)의 값을 생성한다.
- 몇몇 특수 표현식은 여러 개의 값을 받는다.
- 일부 표현식은 여러 개의 값을 전달한다.
- 레슨 10. 프리뷰 - 다른 데이터 타입
- 리스는 숫자에 대해 거의 항상 올바른 동작을 수행한다.
- 문자(characters)는 리스프가 읽고 쓸 수 있는 데이터를 제공한다.
- 배열(arrays)은 데이터를 표로 정리한다.
- 벡터(vectors)는 1차원 배열입니다.
- 문자열(strings)은 문자만 포함하는 벡터입니다.
- 심볼(symbol)은 고유하지만, 여러 값을 가질 수 있습니다.
- 구조체(structures)는 관련 있는 데이터를 저장할 수 있게 해줍니다.
- 타입 정보는 런타임에 확인 가능한다.
- 해시 테이블(hash tables)은 키를 통해 빠르게 데이터에 접근할 수 있게 한다.
- 패키지(packages)는 이름 충돌을 방지한다.
- 레슨 11. 필수 - 입력과 출력
- READ는 Lisp 데이터를 읽어온다.
- PRINT는 당신과 READ를 위해 Lisp 데이터를 출력한다.
- OPEN과 CLOSE는 파일을 다룰 수 있게 해준다.
- PRINT 관련 변종들.
- 레슨 12. 필수 - 리더 매크로
- 리더(reader)는 문자를 데이터로 변환한다.
- 표준 리더 매크로는 내장 데이터 타입을 처리한다.
- 사용자 프로그램은 리더 매크로를 정의할 수 있다.
04장. 핵심 사항 마스터하기
목표: 실습 문제를 통해 3장의 개념을 강화합니다.
- 실습! “toploop”
- 흔히 저지르는 실수를 찾아내고 피하기
- 간단한 함수 정의하기
- 전역 변수와 상수 사용하기
- 재귀 함수 정의하기
- 꼬리 재귀(Tail recursion)
- 네이밍 관련 연습
- 렉시컬 바인딩과 여러 이름 공간
- 읽기, 쓰기, 산술 연산
- 기타 데이터 타입
- 간단한 매크로
- 리더 매크로
05장. 반복문 소개
목표: Introduce the most common looping constructs.
“무한 루프라는 것은 존재하지 않습니다. 결국 컴퓨터가 망가질 테니까요.” – 존 D. 설리반
- 영원히 도는 간단한 LOOP…
- 하지만 탈출구가 있다!
- 카운트 루프에는 DOTIMES를 사용
- 리스트의 요소를 처리하려면 DOLIST를 사용
- DO는 까다롭지만 강력
06장. 구조체의 심층 탐구
목표: 구조체의 가장 유용한 선택적(optional) 기능들을 보여준다.
- 기본값을 설정하면 때때로 초기화자를 생략할 수 있다.
- Lisp가 구조체를 출력하는 방식을 변경하기.
- 구조체가 메모리에 저장되는 방식을 변경하기.
- 슬롯 접근자(slot accessor) 이름을 짧게 만들기.
- 키워드 인자를 사용하지 않고 새 구조체를 할당하기.
- 다른 구조체의 확장으로 구조체를 정의해보기.
07장. 정교한 구조체(structure)로써 오브젝트(Object) 살펴보기
목표: 데이터를 구조화하는 도구로서 CLOS 오브젝트를 소개한다. 오브젝트의 동작(행위)은 이후 장에서 다룸.
- 계층: 분류 vs 포함 관계
- DEFCLASS를 사용하여 새 오브젝트를 정의.
- 오브젝트는 슬롯을 가지며, 구조체보다 더 많은 옵션을 제공.
- 슬롯에 대한 접근을 제어하면 클라이언트의 올바른 사용을 유지하는 데 도움이 됨.
- 슬롯 접근자를 재정의하여 클라이언트가 수행할 수 없는 작업을 수행.
- 특화를 위해 단일 상속을 가진 클래스를 정의.
- 다중 상속은 혼합 및 매칭(mix-and-match) 정의를 허용.
- 옵션은 초기화를 제어하고 문서를 제공.
- 이것은 시작에 불과합니다…
08장. 수명(Lifetime)과 가시성(Visibility)
목표: Lisp 변수의 값이 실행 중 수명과 가시성에 의해 어떻게 영향을 받는지를 보여준다. 다른 언어의 로컬/전역 변수와 매우 유사하지만, Lisp의 스페셜 변수(special variables)가 뭔가 변화시킨다. 이 장은 또한 변수에만 수명과 가시성이 있는게 아님을 이해하는 데 필요한 기초를 다진다.
- Lisp의 모든 것은 수명과 가시성을 가진다
- 수명(Lifetime): 생성, 존재, 그리고 파괴
- 가시성(Visibility): 볼 수 있고 보일 수 있는 것
- 기술적인 명칭: 범위(Extent)와 스코프(Scope)
- 매우 간단한 경우: 최상위 수준 폼 정의(top-level defining forms)
- 매개변수와 LET 변수의 스코프와 범위
- 약간 더 까다로운 경우: 스페셜 변수
09장. 오류 처리와 논-로컬 탈출(Non-Local Exits) 소개
목표: 프로그램의 일부들 사이에서 제어를 이전하는 세 가지 새로운 방법을 보여줍니다.
- UNWIND-PROTECT: 절대, 무조건 실행되어야 할 때
- BLOCK과 RETURN-FROM을 이용한 우아한 종료
- CATCH와 THROW를 이용한 어디에서든 (하지만 아무 때나가 아닌) 탈출
- 파일이 필요한 시간만큼만 열린 상태를 유지하도록 보장하기
10장. 길을 찾는 방법, 1부
목표: 매뉴얼을 참고하지 않고 Lisp에서 있는 것들을 찾는 방법.
- APROPOS: 이름은 기억나지 않지만, 얼굴은 알아볼 수 있을 것 같아
- DESCRIBE: 네 자신에 대해 좀 더 자세히 알려줘
- INSPECT: 입을 크게 벌리고 “아…”라고 말해
- DOCUMENTATION: 어딘가에 그걸 적어 뒀다는 건 알아
11장. 디스트럭티브 모디피케이션(Destructive Modification)
목표: 할당(assignment)과 바인딩(binding)의 차이를 설명하고, 왜 할당이 이해하기 더 어려운지를 보여줌. 그런 다음, 더 어려운 개념인 할당을 선호하는 이유를 탐구하고, 디스트럭티브 모디피케이션을 사용하는 함수들을 소개.
- 단순한 할당은 디스트럭티브 모디피케이션이다
- 할당의 위험성
- 변경 vs. 복사: 효율성의 문제
- 파괴적 함수를 사용하여 리스트 수정하기
- RPLACA, RPLACD, SETF …; 순환성(circularity)
- 장소(places) vs. 값(values): 파괴적 함수가 항상 원하는 사이드이펙트(side-effect)를 갖는 것은 아니다
- 예로 PUSH와 DELETE의 대비
- 공유 및 상수 데이터: 파괴적 변경의 위험
12장. 반복 대신 매핑
목표: 매핑 함수들을 분류하고 시연. 적절한 사용과 부적절한 사용을 보여줍. 시퀀스(sequence)의 개념을 소개.
- MAPCAR, MAPC, MAPCAN은 연속된 리스트 요소들을 처리한다
- MAPLIST, MAPL, MAPCON은 연속된 서브리스트(sublists)를 처리한다
- MAP과 MAP-INTO는 리스트뿐 아니라 시퀀스에 작동한다
- 매핑 함수는 필터링에 좋다
- 효율성을 중시한다면 매핑을 피하는 것이 더 낫다
- 술어(predicate) 매핑 함수는 시퀀스를 테스트한다
- SOME, EVERY, NOTANY, NOTEVERY
- REDUCE는 시퀀스 요소들을 결합한다
13장. 시퀀스로 할 수 있는 더 많은 것들
목표: 가장 유용한 시퀀스 함수들을 소개하고, 어떻게 사용하는지 보여줌. 파괴적 시퀀스 함수가 의도한 효과를 가지기 위해 어떻게 사용되어야 하는지를 보여줌.
- CONCATENATE: 옛 시퀀스로부터 새로운 시퀀스 만들기
- ELT와 SUBSEQ는 어떤 시퀀스에서든 원하는 것을 가져온다 (그리고 COPY-SEQ)
- REVERSE는 시퀀스를 뒤집는다 (그리고 NREVERSE)
- LENGTH: 결국 크기가 중요하다
- COUNT: 겉보다 내면이 중요하다 할 때
- REMOVE, SUBSTITUTE 및 기타 시퀀스 변경 함수
- DELETE, REMOVE-DUPLICATES, DELETE-DUPLICATES, 그리고 NSUBSTITUTE
- FILL과 REPLACE
- 시퀀스에서 위치 파악하기: POSITION, FIND, SEARCH, 그리고 MISMATCH
- SORT와 MERGE가 시퀀스 툴박스를 완성한다
14장. 오브젝트가 정말 스스로 행동할 수 있는가?
목표: 일반 함수(generic functions)가 어떻게 작동하는지 보여줌. 다중 디스패치(multiple dispatch), 상속(inheritance), 메서드 조합(method combination)을 설명. 메타 오브젝트 프로토콜(metaobject protocol)을 미리 보여줌.
- 일반 함수는 오브젝트에 행동을 부여한다
- 멀티 메서드(multimethods)에서는 메서드와 오브젝트 사이의 경계가 흐려진다
- 오브젝트가 아닌 것에 대한 메서드? 그렇다면 메서드는 어디에 존재하는가?
- 일반 함수는 인수의 스페셜라이져(specializers)를 기반으로 디스패치하여 작동한다
- 결국 오브젝트 상속이 중요하다; 적용 가능한 메서드를 찾는 것
- 메서드 조합은 추가적인 선택지를 제공한다
- 어떤 것도 돌에 새겨지지 않았다; 메타 오브젝트 프로토콜 맛보기(peek)
15장. 클로저(Closures)
목표: 클로저가 어떻게 자유 변수(free variables)를 포착하여 다른 실행 컨텍스트에서 사용하는지 보여줌. 몇 가지 실용적인 적용으로 시연.
- 함수의 수명인가, 아니면 수명의 함수인가?
- 프리 변수를 어떻게 찾아내며, 어떻게 처리해야 하는가.
- 클로저를 사용하여 프라이빗하고 안전하게 정보를 유지하기.
- 함수를 반환하는 함수, 그리고 그것이 매크로와 어떻게 다른가.
16장. 길을 찾는 방법, 2부
목표: Lisp 컴파일러가 여러분의 코드를 어떻게 처리하는지, 그리고 코드가 실행될 때 어떤 일을 하는지 지켜보는 법을 배운다.
“DISASSEMBLE은 여러분의 친구입니다.” – Bill St. Clair
- DISASSEMBLE: 항상 궁금했는데, 그 안에 뭐가 들어있는지…
- BREAK와 backtrace: 나는 어떻게 여기까지 온 걸까?
- TRACE와 STEP: 지켜보고 있다!
17장. 모든 비교는 동등하지 않다
목표: Lisp가 왜 그리고 어떻게 이렇게 많은 서로 다른 비교 함수를 가지고 있는지를 설명. 적절한 선택을 위한 지침을 제시.
- 테스트가 길수록, 더 많은 것을 알려준다
- EQ는 동일한 심볼에 대해 참이다
- EQL은 동일한 숫자와 문자에 대해서도 참이다
- EQUAL은 대개 동일하게 출력되는 것에 대해 참이다
- EQUALP는 숫자의 타입과 문자의 대소문자를 무시한다
- 더 긴 테스트는 더 느리다; 무엇을 비교하는지 알아야 한다
- 특수화된 테스트는 더 제한된 데이터 타입에서 더 빠르다
18장. 정말로 논리적이다…
목표: 일반적인 논리 함수와 조건부 평가를 설명. 비트 조작 함수, 비트 벡터, 범용 바이트 조작을 소개.
- AND와 OR는 필요할 만큼만 평가한다
- 비트, 바이트, 그리고 부울
- 비트 벡터는 영원히 계속될 수 있다
- 비트의 덩어리(chunks)는 바이트를 만든다
19장. 스트림(Streams)
목표: 범용 입출력(I/O) 시설로서의 스트림 소개. 서로 다른 장치에 연결된 스트림의 상호 교환성을 강조.
- 스트림은 데이터를 공급하거나 수용하는 파이프를 제공한다
- 파일 스트림 만들기
- 문자열 스트림 만들기
- 바이너리 입출력
20장. 매크로 에티켓(Macro Etiquette)
목표: 3장과 4장의 간단한 예제를 넘어, 다양한 문제를 해결하기 위해 매크로를 올바르게 구성하는 방법을 보여줌.
- 매크로는 프로그램을 생성하는 프로그램이다
- 클로즈업: 매크로가 어떻게 작동하는가
- 백쿼트(backquote)는 치환 템플릿처럼 보인다
- 명백한 것 너머, 1부: 계산하고, 그 다음 생성하기
- 명백한 것 너머, 2부: 매크로를 정의하는 매크로
- 업계의 트릭: GENSYM을 사용하여 캡처(capture)를 회피하기
- 매크로 vs. 인라인(inlining)
21장. 함수와 매크로 인자를 활용한 화려한 트릭
목표: 람다 리스트(lambda-list) 옵션을 설명. 그것들이 어떻게 사용되어 프로그램을 명확하게 하는지 보여줌.
- 키워드는 매개변수에 이름을 붙일 수 있게 한다
- 말하고 싶지 않을 때를 위한 기본값
- 인수를 분해하여 매크로에 구조를 더하기
22장. 길을 찾는 방법, 3부
목표: 오브젝트와 메서드에 대해 어떻게 알아보는지 배움. 소스 코드를 변경하지 않고 프로그램 동작을 변경하거나 모니터링하는 특수화된 기법을 배움.
- 클래스와 메서드 브라우저는 오브젝트의 바다에서 길을 찾는 데 도움이 된다
- ADVISE는 함수를 변경하지 않고 함수의 동작을 수정할 수 있게 한다
- WATCH는 흥미로운 변수에 대한 창을 열게 한다
23장. 오류는 예상되는 것이다; 복구는 신성한 일이다
목표: 사용자 정의 오류 감지 및 복구 메커니즘을 어떻게 생성하는지 보여줌.
- 에러를 알리고(signal) 사용자에게 인식시킨다
- Conditions를 사용하여 에러를 분류한다
- Restarts를 사용하여 Conditions에서 복구한다
24장. FORMAT은 특이한 언어로 말한다
목표: FORMAT 함수의 가장 유용한 함수들을 설명.
- FORMAT은 FORTRAN과 운율이 같다, 어느 정도는…
- 포맷팅
- 반복
- 조건부
- 잡다한것(Floobydust)
25장. Lisp을 현실 세계와 연결하기
목표: FFI(Foreign Function Interface)을 설명하고, 실제 구현체로부터의 예제를 제공. 래퍼(wrappers)를 사용하여 C++ 함수를 호출하는 방법을 보여줌. 콜백(callbacks)이 어떻게 C 프로그램이 Lisp 함수를 호출할 수 있게 하는지 보여줌. TCP/IP 접근을 이용한 예를 제공.
- Foreign Function Interfaces는 “외국어“로 작성된 프로그램과 대화할 수 있게 한다
- 이걸 래핑(wrap)해 줄래요?
- 나중에 다시 전화할게… (콜백)
- 네트워크 인터페이스: 이 네개의 벽 너머로
26장. 행복한 얼굴을 다으라: 인터페이스 빌더
목표: 이벤트 기반 인터페이스와 GUI 빌더에 대한 논의 후, 주요 데스크톱 Common Lisp 플랫폼에서의 예제를 보여줌. Garnet과 CLIM과 같은 플랫폼 독립적인 Lisp GUI에 대한 논의로 마무리.
- 이벤트 기반 인터페이스
- 그래픽 프로그래밍
- 예제: MCL의 Interface Toolkit
- 플랫폼 독립적인 인터페이스
27장. 좋은 편집기는 천 번의 키 입력보다 낫다
목표: Lisp의 단순한 구문(integrated editor)이 통합 편집기와 어떻게 결합하여 Lisp 프로그램 작성의 많은 일반적 작업을 쉽게 만드는지 보여줌.
- 매칭(matching)과 플래싱(flashing)
- 자동 들여쓰기
- 심볼 자동완성
- 정의 찾기
- 온라인 문서
- 디버깅 도구 접근
- Lisp를 사용하여 편집기 확장
28장. 프로그래밍을 위한 실용적인 기법
목표: Lisp 스타일에 대해 유용한 지침을 제공함. 디버깅, 성능, 그리고 가독성 사이의 트레이드오프에 대해 실용적인 조언을 제시.
- Lisp 스타일의 요소
- 프로퍼티 리스트(property lists)는 작고(매우 작은) 임의적(ad-hoc) 데이터베이스에 유용하다
- 선언(declarations)은 때때로 컴파일러를 돕는다
- DEFVAR과 DEFPARAMETER
- DEFCONSTANT로 상수 정의하기
- 컴파일러를 사용(안)할 때를 알라
- 속도 vs. 디버깅 능력
- 효율성: 특정하기, 테스트하기
- 비효율성 인식, 프로파일링(profiling); 성능 vs. 가독성
29장. 쓰레기를 치울 차례가 너인 줄 알았어
목표: 가비지 컬렉션의 이점과 비용을 설명함. 프로그램이 생성하는 가비지 양을 줄임으로써 프로그램 성능을 개선하는 방법을 보여줌.
- 가비지는 무엇인가?
- 왜 가비지 컬렉션이 중요한가?
- 가비지 컬렉션은 어떻게 작동하는가?
- 가비지가 내 프로그램에 어떤 영향을 미치는가?
- 내 프로그램에서 가비지 컬렉션 일시중단을 어떻게 줄일 수 있는가?
30장. 디버깅과 버그 방지 위한 유용한 힌트
목표: Lisp의 디버깅 도구 사용법을 설명합니다.
- 오류 원인 찾기
- 백트레이스 읽기, 디버깅을 위한 컴파일러 설정
- 단순한 디버깅 도구
- BREAK, PRINT
- 까다로운 문제용 파워 도구
- TRACE, STEP, ADVISE, WATCH
- 괴물의 배속으로
- INSPECT, DESCRIBE
- 오류로부터 계속하기
- 원치 않는 정의에 따른 문제
- 패키지 문제; 메서드 정의
- 매크로의 문제
- 런타임 테스트는 “발생할 수 없는 경우“가 실제로 발생할 때 이를 포착한다
- case dispatch 대신 메서드 디스패치 사용
31장. Lisp으로 대형 프로젝트 처리하기
목표: 대형 프로젝트와 팀 작업에서 Lisp 프로그램을 조직하기 위해 사용되는 전략과 도구를 설명.
- 패키지(Packages)는 당신의 이름을 제 이름과 분리해 줌.
- 시스템 빌더(System builders)는 의존성을 기술할 수 있게 해줌
- 소스 제어 시스템(Source control systems)은 여러 버전(revisions)을 추적함
- 모듈(Modules): 파일 의존성을 기술하는 또 다른 방식
- PROVIDE와 REQUIRE
32장. 어두운 구석들과 호기심 유발 요소들
목표: 더 최신이거나, 표준화되지 않았거나, 실험적이거나, 논쟁적인 Lisp 기능들을 설명함.
- 확장된 LOOP: 또 다른 작은 언어
- TAGBODY: 꼭 해야 한다면 GO
- 프로세스 & 스택 그룹: 여러 작업을 저글링하기
- 시리즈(Series): 반복과 필터링에 대한 또 다른 접근법
33장. 다음으로
목표: 다른 정보 출처와 제품들을 가리키는 방향을 제공.
- 추가 읽을거리 제안
- 온라인 소스
- 상업적 벤더
34장. Lisp 역사, 혹은: 오해의 기원
목표: Lisp의 개발에 대한 간략한 역사를 제공하여, 오래된 몇 가지 오해에 대한 통찰을 줌.
- 존 매카시(John McCarthy)의 표기법
- 초기 구현체들
- 특수 하드웨어
- 갈라진 방언들(dialects)
- DARPA의 지시
- 동서양, 그리고 유럽의 경쟁
- 표준 하드웨어를 위한 컴파일러의 등장
- 표준화를 향한 긴 여정
- 최첨단(State of the Art)?
부록 A. 성공적인 Lisp 응용 프로그램
목표: Lisp으로 구축된 대형 성공 사례 응용 프로그램들을 설명.
- Emacs
- G2
- AutoCad
- Igor Engraver
- Yahoo Store
- …