Lisp에 관한 Paul Graham의 요점을 설명하십시오


146

Paul Graham의 Lisp를 다르게 만든 것의 요점을 이해하는 데 도움이 필요합니다 .

  1. 새로운 변수 개념. Lisp에서 모든 변수는 사실상 포인터입니다. 값은 변수가 아닌 유형을 갖는 것이므로 변수를 할당하거나 바인딩하는 것은 포인터가 가리키는 것이 아니라 포인터를 복사하는 것을 의미합니다.

  2. 심볼 타입. 포인터를 비교하여 동등성을 테스트 할 수 있다는 점에서 기호는 문자열과 다릅니다.

  3. 기호 트리를 사용하는 코드 표기법.

  4. 항상 사용 가능한 언어입니다. 읽기 시간, 컴파일 시간 및 런타임은 실제로 구별되지 않습니다. 코드를 읽거나 컴파일하는 동안 코드를 컴파일 또는 실행하고, 컴파일하는 동안 코드를 읽거나 실행하며, 런타임에 코드를 읽거나 컴파일 할 수 있습니다.

이 요점은 무엇을 의미합니까? C 또는 Java와 같은 언어에서 어떻게 다른가요? Lisp 패밀리 언어 이외의 다른 언어에 이러한 구성이 있습니까?


10
사실이 - 나는 기능 프로그래밍 태그가 기능 코드를 작성하는 것입니다 많은 Lisps에 필수적이나 OO 코드를 작성하는 동등하게 가능한 한, 여기에 보증 모르겠어요 많은 비 기능 리스프의가 주위에 코드. fp 태그를 제거하고 clojure를 대신 추가하는 것이 좋습니다. JVM 기반 Lispers에서 흥미로운 정보를 얻을 수 있기를 바랍니다.
Michał Marczyk

58
paul-graham여기 에 태그가 있습니까? !!! 위대한 ...
missingfaktor

@missingfaktor 아마도 그것은 burninate 요청
cat

답변:


98

Matt의 설명은 완벽합니다. 그는 C와 Java와 비교해 보았습니다.하지만 내가하지 않을 것입니다. 어떤 이유로 나는이 주제에 대해 한 번 이야기하는 것을 정말로 좋아합니다. 대답에.

포인트 (3)과 (4)에서 :

목록의 포인트 (3)과 (4)가 가장 흥미롭고 여전히 관련성이 있습니다.

그것들을 이해하려면 프로그래머가 입력 한 문자 스트림 형태로 Lisp 코드로 발생하는 일을 명확하게 파악하는 것이 유용합니다. 구체적인 예를 보자.

;; a library import for completeness,
;; we won't concern ourselves with it
(require '[clojure.contrib.string :as str])

;; this is the interesting bit:
(println (str/replace-re #"\d+" "FOO" "a123b4c56"))

이 스 니펫 Clojure 코드 출력됩니다 aFOObFOOcFOO. 읽기 시간이 실제로 사용자 코드에 개방적이지 않기 때문에 Clojure는 목록의 네 번째 요점을 완전히 만족 시키지는 않습니다. 그러나 이것이 그렇지 않다는 것이 무엇을 의미하는지 논의 할 것입니다.

따라서이 코드를 파일 어딘가에 가지고 있고 Clojure에게 코드를 실행하도록 요청한다고 가정하십시오. 또한 (간단 성을 위해) 라이브러리 가져 오기를 지난 것으로 가정했습니다. 흥미로운 비트는(println) 는 오른쪽 에서 하여 오른쪽 끝에서 끝납니다 . 이것은 예상대로 어휘 / 구문 분석되었지만 이미 중요한 요점이 발생합니다. 결과는 특별한 컴파일러 특정 AST 표현이 아닙니다. 정기적 인 Clojure / Lisp 데이터 구조 일뿐 입니다. 문자열,이 경우에는 컴파일 된 단일 정규식 패턴 객체#"\d+"리터럴 (자세한 내용은 아래 참조). 일부 Lisp은이 프로세스에 약간의 왜곡을 가하지 만 Paul Graham은 대부분 Common Lisp를 언급했습니다. 귀하의 질문과 관련하여 Clojure는 CL과 유사합니다.

컴파일 타임에 전체 언어 :

이 시점 이후 모든 컴파일러는 Lisp 인터프리터의 경우에도 마찬가지입니다. Clojure 코드는 항상 컴파일됩니다) Lisp 데이터 구조는 Lisp 프로그래머가 조작하는 데 사용됩니다. 이 시점에서 놀라운 가능성이 명백해집니다. Lisp 프로그래머가 Lisp 프로그램을 나타내는 Lisp 데이터를 조작하고 변환 된 프로그램을 나타내는 변환 된 데이터를 원본 대신 사용할 수있는 Lisp 함수를 작성할 수없는 이유는 무엇입니까? 다시 말해, Lisp 프로그래머가 함수를 Lisp의 매크로라고하는 일종의 컴파일러 플러그인으로 등록 할 수없는 이유는 무엇입니까? 실제로 어떤 Lisp 시스템에도이 용량이 있습니다.

따라서 매크로는 실제 객체 코드가 생성 될 때 최종 컴파일 단계 전에 컴파일 타임에 프로그램 표현에서 작동하는 일반적인 Lisp 함수입니다. 실행할 수있는 코드 매크로의 종류에는 제한이 없으므로 (특히, 매크로 코드를 자유롭게 사용하여 코드 자체를 작성하는 경우가 많음) "컴파일 타임에 전체 언어를 사용할 수 있습니다" ".

읽을 때 전체 언어 :

#"\d+"정규식 리터럴 로 돌아가 봅시다 . 위에서 언급했듯이, 컴파일러가 컴파일을 위해 준비중인 새 코드에 대한 첫 번째 언급을 듣기 전에 읽기 시간에 실제 컴파일 된 패턴 객체로 변환됩니다. 어떻게 이런 일이 발생합니까?

Clojure가 현재 구현되는 방식은 Paul Graham이 생각한 것과 약간 다르지만 영리한 해킹으로 가능합니다 . Common Lisp에서 이야기는 개념적으로 약간 깨끗합니다. 그러나 기본 사항은 비슷합니다. Lisp Reader는 상태 전이를 수행하고 결국 "수락 상태"에 도달했는지 여부를 선언하는 상태 머신은 문자가 나타내는 Lisp 데이터 구조를 뱉어냅니다. 따라서 문자 123는 숫자 123등이됩니다. 이제 중요한 점이 있습니다. 이 상태 머신은 사용자 코드로 수정할 수 있습니다. (앞서 언급했듯이 CL의 경우에는 전적으로 사실입니다. Clojure의 경우 해킹 (감소 및 실제로 사용되지 않음)이 필요합니다. 그러나 나는 PG의 기사이므로 자세히 설명해야합니다 ...)

따라서 공통 Lisp 프로그래머이고 Clojure 스타일의 벡터 리터럴에 대한 아이디어가 마음에 들면 독자에게 일부 문자 시퀀스에 적절하게 반응하는 함수를 연결 [하거나 #[처리 할 수 ​​있습니다. 일치에서 끝나는 문자 벡터의 시작 ]. 이러한 기능을 리더 매크로 라고하며 일반 매크로 와 마찬가지로 이전에 등록 된 리더 매크로에서 활성화 된 펑키 표기법으로 작성된 코드를 포함하여 모든 종류의 Lisp 코드를 실행할 수 있습니다. 그래서 당신을 위해 읽기에 전체 언어가 있습니다.

그것을 마무리 :

실제로, 지금까지 증명 된 것은 읽기 또는 컴파일 시간에 일반 Lisp 함수를 실행할 수 있다는 것입니다. 읽기, 컴파일 또는 런타임에서 읽기 및 컴파일이 어떻게 가능한지 이해하기 위해 여기에서 취해야 할 한 단계는 읽기 및 컴파일이 Lisp 함수에 의해 수행된다는 것을 인식하는 것입니다. 문자 스트림에서 Lisp 데이터를 읽거나 Lisp 코드를 각각 컴파일하고 실행하기 위해 언제든지 호출 read하거나 호출 할 수 있습니다 eval. 그것은 항상 모든 언어입니다.

Lisp가 목록에서 포인트 (3)을 충족한다는 점이 포인트 (4)를 충족시키는 방식에 필수적이라는 점에 주목하십시오. Lisp가 제공하는 특정 매크로는 일반적인 Lisp 데이터로 표현되는 코드에 크게 의존합니다. 그것은 (3)에 의해 가능하게 된 것입니다. 우연히도 코드의 "트리-쉬쉬 (tree-ish)"측면 만 중요합니다. XML을 사용하여 Lisp를 작성할 수있을 것입니다.


4
주의 : "정규 (컴파일러) 매크로"라고 말하면 공통 Lisp (적어도)에서 "컴파일러 매크로"가 매우 구체적이고 다른 것 인 lispworks
Ken

켄 : 잘 잡아라, 고마워! 나는 이것을 "정규 매크로"로 바꾸겠다. 나는 누군가를 넘어 뜨리지 않을 것이라고 생각한다.
Michał Marczyk

환상적인 답변. 나는 몇 시간 동안 인터넷 검색 / 질문을 생각하는 것보다 5 분 안에 더 많은 것을 배웠습니다. 감사.
Charlie Flowers

편집 : 아아, 실행 문장을 오해했습니다. 문법이 수정되었습니다 ( "피어"가 필요합니다).
Tatiana Racheva 2016 년

S- 표현식과 XML은 동일한 구조를 지시 할 수 있지만 XML은 훨씬 더 장황하므로 구문으로 적합하지 않습니다.
Sylwester

66

1) 새로운 변수 개념. Lisp에서 모든 변수는 사실상 포인터입니다. 값은 변수가 아닌 유형을 갖는 것이므로 변수를 할당하거나 바인딩하는 것은 포인터가 가리키는 것이 아니라 포인터를 복사하는 것을 의미합니다.

(defun print-twice (it)
  (print it)
  (print it))

'it'은 변수입니다. 모든 값에 바인딩 될 수 있습니다. 변수와 관련된 제한 및 유형이 없습니다. 함수를 호출하면 인수를 복사 할 필요가 없습니다. 변수는 포인터와 유사합니다. 변수에 바인딩 된 값에 액세스하는 방법이 있습니다. 메모리 를 예약 할 필요가 없습니다 . 함수를 호출 할 때 모든 크기와 유형의 모든 데이터 객체를 전달할 수 있습니다.

데이터 개체에는 '유형'이 있으며 모든 데이터 개체는 해당 '유형'에 대해 쿼리 할 수 ​​있습니다.

(type-of "abc")  -> STRING

2) 심볼 타입. 포인터를 비교하여 동등성을 테스트 할 수 있다는 점에서 기호는 문자열과 다릅니다.

심볼은 이름을 가진 데이터 객체입니다. 일반적으로 이름을 사용하여 객체를 찾을 수 있습니다.

|This is a Symbol|
this-is-also-a-symbol

(find-symbol "SIN")   ->  SIN

심볼은 실제 데이터 객체이므로 동일한 객체인지 테스트 할 수 있습니다.

(eq 'sin 'cos) -> NIL
(eq 'sin 'sin) -> T

이를 통해 예를 들어 기호가있는 문장을 작성할 수 있습니다.

(defvar *sentence* '(mary called tom to tell him the price of the book))

이제 문장에서 THE의 수를 셀 수 있습니다 :

(count 'the *sentence*) ->  2

공통 Lisp 기호에서 이름은 물론 값, 함수, 속성 목록 및 패키지도 가질 수 있습니다. 따라서 기호를 사용하여 변수 또는 함수의 이름을 지정할 수 있습니다. 속성 목록은 일반적으로 메타 데이터를 심볼에 추가하는 데 사용됩니다.

3) 심볼 트리를 사용하는 코드 표기법.

Lisp는 기본 데이터 구조를 사용하여 코드를 나타냅니다.

목록 (* 3 2)은 데이터와 코드가 될 수 있습니다.

(eval '(* 3 (+ 2 5))) -> 21

(length '(* 3 (+ 2 5))) -> 3

나무:

CL-USER 8 > (sdraw '(* 3 (+ 2 5)))

[*|*]--->[*|*]--->[*|*]--->NIL
 |        |        |
 v        v        v
 *        3       [*|*]--->[*|*]--->[*|*]--->NIL
                   |        |        |
                   v        v        v
                   +        2        5

4) 항상 사용 가능한 언어. 읽기 시간, 컴파일 시간 및 런타임은 실제로 구별되지 않습니다. 코드를 읽거나 컴파일하는 동안 코드를 컴파일 또는 실행하고, 컴파일하는 동안 코드를 읽거나 실행하며, 런타임에 코드를 읽거나 컴파일 할 수 있습니다.

Lisp는 텍스트에서 데이터와 코드를 읽기위한 READ,로드하기 위해 코드로드, 코드를 평가하기위한 EVAL, 코드를 컴파일하기위한 COMPILE 및 데이터와 코드를 텍스트에 쓰는 PRINT 기능을 제공합니다.

이 기능은 항상 사용 가능합니다. 그들은 멀리 가지 않습니다. 그들은 모든 프로그램의 일부가 될 수 있습니다. 즉, 모든 프로그램이 항상 코드를 읽거나로드하거나 평가하거나 인쇄 할 수 있습니다.

C 또는 Java와 같은 언어에서 어떻게 다른가요?

이러한 언어는 기호, 데이터 코드 또는 데이터 런타임 코드를 코드로 제공하지 않습니다. C의 데이터 개체는 일반적으로 형식이 지정되지 않습니다.

LISP 계열 언어 이외의 다른 언어에 이러한 구성이 있습니까?

많은 언어에는 이러한 기능 중 일부가 있습니다.

차이점:

Lisp에서 이러한 기능은 사용하기 쉽도록 언어로 설계되었습니다.


33

포인트 (1)과 (2)에서 그는 역사적으로 이야기하고 있습니다. Java의 변수는 거의 동일하므로 값을 비교하려면 .equals ()를 호출해야합니다.

(3) S- 표현에 대해 이야기하고 있습니다. Lisp 프로그램은이 구문으로 작성되는데, 이는 C 매크로 나 C ++ 템플릿보다 훨씬 깔끔한 방식으로 매크로에서 반복 패턴을 캡처하고 동일한 코어 목록으로 코드를 조작하는 것과 같이 Java 및 C와 같은 임시 구문에 비해 많은 이점을 제공합니다. 데이터에 사용하는 작업

(4) 예를 들어 C를 사용하면 언어는 실제로 두 가지 다른 하위 언어입니다 : if () 및 while ()과 같은 것과 전 처리기. 전처리기를 사용하여 항상 반복하거나 # if / # ifdef로 코드를 건너 뛸 필요가 없습니다. 그러나 두 언어는 완전히 분리되어 있으므로 #if와 같이 컴파일 타임에 while ()을 사용할 수 없습니다.

C ++은 템플릿을 사용 하여이 문제를 더욱 악화시킵니다. 컴파일 타임에 코드를 생성하는 방법을 제공하고 전문가가 아닌 사람들이 머리를 감쌀 수없는 템플릿 메타 프로그래밍에 대한 몇 가지 참조를 확인하십시오. 또한, 컴파일러가 일급 지원을 제공 할 수없는 템플릿과 매크로를 사용하는 것은 실제로 해킹과 트릭입니다. 간단한 구문 오류를 만들면 컴파일러가 명확한 오류 메시지를 표시 할 수 없습니다.

Lisp를 사용하면이 모든 것이 하나의 단일 언어로 제공됩니다. 첫날에 배울 때와 동일한 것을 사용하여 런타임에 코드를 생성합니다. 이것은 메타 프로그래밍이 사소한 것이 아니라고 일급 언어 및 컴파일러 지원으로 더욱 간단합니다.


7
또한,이 능력 (및 단순성)은 이제 50 년이 넘었으며 초보자 프로그래머가 최소한의 안내만으로 언어를 익힐 수 있고 언어 기초에 대해 배울 수있을 정도로 쉽게 구현할 수 있습니다. 훌륭한 초보자 프로젝트로 Java, C, Python, Perl, Haskell 등의 비슷한 주장을 듣지 못할 것입니다!
매트 커티스

9
Java 변수가 Lisp 기호와 전혀 같지 않다고 생각합니다. Java에는 기호에 대한 표기법이 없으며 변수로 할 수있는 유일한 것은 값 셀을 얻는 것입니다. 문자열은 서로 맞 물릴 수 있지만 일반적으로 이름이 아니므로 인용, 평가, 전달 등의 여부에 대해 이야기하는 것은 합리적이지 않습니다.
Ken

2
40 세 이상이 더 정확할 수도 있습니다 :), @Ken : 1) 자바의 기본 변수가 아닌 변수는 lisp와 유사하며 2) Java의 인터 닝 된 문자열은 lisp의 기호와 유사합니다. 물론, 당신이 말했듯이, 자바에서 인턴 된 문자열 / 코드를 인용하거나 평가할 수 없으므로 여전히 다릅니다.

3
@Dan-최초의 구현 시점이 확실하지 않지만 상징적 계산에 관한 초기 McCarthy 논문 이 1960 년에 출판되었습니다.
Inaimathi

Java는 Foo.class / foo.getClass () 형식의“기호”에 대한 부분적 / 불규칙적 인 지원을 가지고 있습니다. 즉, type-of-a-type Class <Foo> 객체는 열거 형 값처럼 학위. 그러나 Lisp 심볼의 그림자는 매우 적습니다.
BRPocock

-3

포인트 (1)과 (2)도 파이썬에 적합합니다. 간단한 예 "a = str (82.4)"를 사용하면 인터프리터는 먼저 값이 82.4 인 부동 소수점 객체를 만듭니다. 그런 다음 문자열 생성자를 호출하여 값이 '82 .4 '인 문자열을 반환합니다. 왼쪽의 'a'는 해당 문자열 객체의 레이블 일뿐입니다. 더 이상 참조가 없으므로 원래 부동 소수점 오브젝트가 가비지 수집되었습니다.

구성표에서 모든 것은 비슷한 방식으로 객체로 취급됩니다. Common Lisp에 대해 잘 모르겠습니다. C / C ++ 개념의 관점에서 생각하지 않으려 고합니다. 나는 Lisps의 아름다운 단순성 주위에서 내 머리를 얻으려고 할 때 힙을 느리게했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.