Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

목차


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
  • …