macros - lisp 문법




Lisp 매크로를 특별하게 만드는 이유는 무엇입니까? (10)

C 또는 C ++에서 매크로 및 템플릿을 사용하여 수행 할 수있는 작업을 생각해보십시오. 그것들은 반복적 인 코드를 관리하는 데 매우 유용한 도구이지만, 매우 심각한 방식으로 제한되어 있습니다.

  • 매크로 / 템플릿 구문이 제한되어있어 사용이 제한됩니다. 예를 들어 클래스 나 함수가 아닌 다른 것으로 확장되는 템플릿을 작성할 수 없습니다. 매크로와 템플릿은 내부 데이터를 쉽게 유지 관리 할 수 ​​없습니다.
  • C와 C ++의 복잡하고 매우 불규칙한 구문은 매우 일반적인 매크로를 작성하는 것을 어렵게 만듭니다.

Lisp 및 Lisp 매크로는 이러한 문제를 해결합니다.

  • Lisp 매크로는 Lisp로 작성됩니다. 당신은 매크로를 작성하기위한 Lisp의 힘을 가지고있다.
  • Lisp에는 매우 일반적인 구문이 있습니다.

C ++을 마스터 한 사람에게 이야기하고 템플릿 메타 프로그래밍을 수행하는 데 필요한 모든 템플릿을 배우는 데 얼마나 오랜 시간이 걸렸는지 물어보십시오. 또는 언어가 10 년 동안 표준화되었지만 실제 컴파일러간에 디버깅하기가 어렵고 (실제로) 실제 컴파일러간에 이식성이없는 Modern C ++ Design 과 같은 (훌륭한) 서적에있는 모든 미친 트릭. 메타 프로그래밍에 사용하는 언어가 프로그래밍에 사용하는 언어와 동일하면 모든 것이 사라집니다!

프로그래밍 언어에 대한 Paul Graham의 글 을 읽으면 Lisp 매크로 가 유일한 방법이라고 생각할 것입니다. 다른 플랫폼에서 일하는 바쁜 개발자로서 나는 Lisp 매크로를 사용할 권한이 없다. 버즈를 이해하려는 사람으로서이 기능을 너무 강력하게 만드는 이유를 설명하십시오.

이것을 파이썬, 자바, C # 또는 C 개발의 세계에서 이해할 수있는 무언가와도 관련 시키십시오.


Common Lisp 매크로는 본질적으로 코드의 "구문 론적 프리미티브"를 확장합니다.

예를 들어, C에서 switch / case 구조는 정수 유형에서만 작동하며, 부동 또는 문자열에 사용하려는 경우에는 중첩 if 문과 명시 적 비교가 사용됩니다. 또한 C 매크로를 작성하여 작업을 수행 할 수있는 방법이 없습니다.

그러나 lisp 매크로는 코드의 스 니펫 (snippets)을 입력으로 받아 매크로의 "호출"을 대체하는 코드를 반환하는 (본질적으로) isp 프로그램이기 때문에 원하는만큼 "프리미티브"레퍼토리를 확장 할 수 있습니다. 보다 읽기 쉬운 프로그램으로

C에서 동일한 작업을 수행하려면 초기 (매우 C가 아닌) 소스를 먹는 사용자 정의 사전 프로세서를 작성해야하며 C 컴파일러가 이해할 수있는 것을 뱉어 내야합니다. 그것에 대해 갈 길은 잘못이 아니지만 반드시 가장 쉬운 것은 아닙니다.


Lisp 매크로를 사용하면 어떤 부분이나 표현식을 언제 평가할지 결정할 수 있습니다. 간단한 예를 들면, C의 생각 :

expr1 && expr2 && expr3 ...

이것이 말하는 것은 : expr1 평가하고, 그것이 사실이라면 expr2 등을 평가하십시오.

이제 이것을 && 함수로 만들려고 노력하십시오 ... 맞습니다. 당신은 할 수 없습니다. 같은 전화 :

and(expr1, expr2, expr3)

expr1 이 false인지 여부에 관계없이 세 번의 exprs 를 모두 평가합니다.

lisp 매크로를 사용하면 다음과 같은 코드를 작성할 수 있습니다.

(defmacro && (expr1 &rest exprs)
    `(if ,expr1                     ;` Warning: I have not tested
         (&& ,@exprs)               ;   this and might be wrong!
         nil))

이제는 함수처럼 호출 할 수있는 && 가 있으며, 모두 true가 아닌 한 전달되는 양식을 평가하지 않습니다.

이것이 어떻게 유용한지를 보려면 대조 :

(&& (very-cheap-operation)
    (very-expensive-operation)
    (operation-with-serious-side-effects))

과:

and(very_cheap_operation(),
    very_expensive_operation(),
    operation_with_serious_side_effects());

매크로로 할 수있는 다른 일은 새로운 키워드 및 / 또는 미니 언어 (예를 들어 (loop ...) 매크로를 확인하십시오)를 작성하고, 다른 언어를 lisp에 통합하는 것입니다. 예를 들어, 매크로를 작성하여 다음과 같이 말하십시오.

(setvar *rows* (sql select count(*)
                      from some-table
                     where column1 = "Yes"
                       and column2 like "some%string%")

그리고 Reader 매크로 에도 들어 가지 않습니다.

희망이 도움이됩니다.


lisp 매크로는 프로그램 조각을 입력으로 사용합니다. 이 프로그램 조각은 원하는대로 조작하고 변형 할 수있는 데이터 구조로 나타납니다. 결국 매크로는 다른 프로그램 단편을 출력하고이 단편은 런타임에 실행되는 것입니다.

C #에는 매크로 기능이 없습니다. 그러나 컴파일러가 코드를 CodeDOM 트리로 구문 분석 한 다음 해당 코드를 다른 CodeDOM으로 변환 한 다음이를 IL로 컴파일 한 경우 해당 코드가 해당됩니다.

이것은 기본 코드로 변환되는 매크로로 -clause, linq select expression 등을 using for each 구문 for each "설탕"구문을 구현하는 데 사용할 수 있습니다.

Java에 매크로가 있으면 Sun에서 기본 언어를 변경하지 않고 Java로 Linq 구문을 구현할 수 있습니다.

다음은 using 을 구현하기위한 C #의 lisp 스타일 매크로가 어떻게 보이는지에 대한 의사 코드입니다.

define macro "using":
    using ($type $varname = $expression) $block
into:
    $type $varname;
    try {
       $varname = $expression;
       $block;
    } finally {
       $varname.Dispose();
    }

나는 일반적인 lisp 요리 책에서 이것을 얻었지만, 나는 왜 lisp 매크로가 좋은 방법으로 좋은지 설명했다.

"매크로는 다른 Lisp 코드 조각에 작용하는 Lisp 코드의 평범한 조각으로, 실행 가능한 Lisp (더 가까운 버전)로 변환합니다. 다소 복잡해 보일 수 있으므로 간단한 예를 들어 보겠습니다. 두 변수를 같은 값으로 설정하는 setq 버전.

(setq2 x y (+ z 3))

z=8 때 x와 y는 모두 11로 설정됩니다. (이것에 대한 사용법은 생각할 수 없지만 단지 예제 일뿐입니다.)

우리는 setq2를 함수로 정의 할 수 없다는 것을 분명히해야합니다. x=50y=-5 인 경우이 함수는 50, -5 및 11 값을받습니다. 어떤 변수가 설정되어야하는지 알지 못합니다. 우리가 정말로 말하고 싶은 것은, 당신 (Lisp 시스템)이 (setq2 v1 v2 e) , 그것을 (progn (setq v1 e) (setq v2 e)) 와 같은 것으로 취급한다는 것이다. 사실, 이것은 옳지 않습니다,하지만 지금은 할 것입니다. 매크로는 입력 패턴 (setq2 v1 v2 e) "출력 패턴 (progn ...) "으로 변환하는 프로그램을 지정하여 정확하게 처리 할 수있게 해줍니다.

이것이 좋은 생각이라면 여기 계속 읽을 수 있습니다 : http://cl-cookbook.sourceforge.net/macros.html


모든 사람 (훌륭한) 게시물에 대한 통찰력을 추가 할 수 있는지 잘 모르겠지만 ...

Lisp 매크로는 Lisp 구문의 특성 때문에 훌륭하게 작동합니다.

리스프는 매우 규칙적인 언어입니다 (모든 것이 리스트 라고 생각 하십시오 ). 매크로를 사용하면 데이터와 코드를 동일하게 취급 할 수 있습니다 (구문 분석이나 다른 해킹이 없어도 lisp 표현식을 수정할 수 있습니다). 이 두 가지 기능을 결합하면 코드를 수정할 수있는 매우 깨끗한 방법이 있습니다.

편집 : 내가 말하려고했던 것은 Lisp가 homoiconic )이라는 것인데, 이는 Lisp 프로그램의 데이터 구조가 Lisp 자체로 작성되었음을 의미합니다.

그래서 언어 자체를 모든 언어로 사용하여 자신 만의 코드 생성기를 만드는 방법으로 끝납니다 (예 : 자바에서는 바이트 코드로 짜 맞춰야합니다. AspectJ와 같은 일부 프레임 워크에서는 다른 접근법을 사용하여이 작업을 수행하십시오. 기본적으로 해킹입니다.)

실제로 매크로를 사용하면 추가적인 언어 나 도구를 배울 필요없이 언어 자체의 모든 기능을 사용하여 자신 만의 미니 언어를 만들 수 있습니다.



즉, 매크로는 코드의 변형입니다. 이것들은 많은 새로운 구문 구조를 도입 할 수있게합니다. 예를 들어, C #에서 LINQ를 고려하십시오. lisp에는 매크로로 구현 된 유사한 언어 확장이 있습니다 (예 : 내장 루프 구조, 반복). 매크로는 코드 중복을 크게 줄입니다. 매크로는«little languages»를 포함 할 수 있습니다 (예를 들어, C # / Java에서는 lisp에서 xml을 사용하여 매크로를 사용하여 동일한 것을 달성 할 수 있습니다). 매크로는 라이브러리 사용에 어려움을 숨길 수 있습니다.

예를 들어, 당신은 쓸 수있다.

(iter (for (id name) in-clsql-query "select id, name from users" on-database *users-database*)
      (format t "User with ID of ~A has name ~A.~%" id name))

C #에서는 SqlConnections, SqlCommands를 생성하고, SqlParameters를 SqlCommands에 추가하고, SqlDataReaders를 루핑하여 올바르게 닫습니다. 반면에 모든 데이터베이스 항목 (트랜잭션, 적절한 연결 닫기, 데이터 가져 오기 등)이 숨겨집니다.


파이썬에는 데코레이터가 있습니다. 기본적으로 다른 함수를 입력으로 사용하는 함수가 있습니다. 당신이 원하는 것을 할 수 있습니다 : 함수를 호출하거나, 다른 것을하거나, 자원 획득 릴리즈에서 함수 호출을 감싸는 등등. 그러나 당신은 그 함수 내부를 들여다 볼 필요가 없습니다. 데코레이터가 목록의 함수 코드를받은 다음 함수를 그대로 실행할 수는 없지만 이제 함수의 일부를 실행하고 함수의 줄을 재정렬 할 수 있다고 말했습니다.


여기서 lisp 매크로에 대한 포괄적 인 토론을 볼 수 있습니다 .

그 기사의 흥미로운 하위 집합 :

대부분의 프로그래밍 언어에서 구문은 복잡합니다. 매크로는 프로그램 구문을 분해하고 분석하고 다시 어셈블해야합니다. 그들은 프로그램 파서에 접근 할 수 없으므로, 그들은 경험적 방법론과 최선의 추측에 의존해야합니다. 때로는 컷 - 율 분석이 잘못되어 끊어지는 경우가 있습니다.

그러나 Lisp은 다릅니다. Lisp 매크로 파서에 접근 할 수 있으며, 이것은 매우 간단한 파서입니다. Lisp 프로그램의 소스는 문자열이 아니기 때문에, Lisp 매크로는 문자열이 아니라 목록 형태의 소스 코드로 준비되어 있습니다. 그것은 목록입니다. 그리고 Lisp 프로그램은 목록을 분리하고 함께 다시 사용하는 데 정말 좋습니다. 그들은 이것을 매일 믿을 수있게합니다.

다음은 확장 된 예제입니다. Lisp에는 "setf"라고하는 할당을 수행하는 매크로가 있습니다. setf의 가장 간단한 형식은 다음과 같습니다.

  (setf x whatever)

기호 "x"의 값을 표현 "whatever"의 값으로 설정합니다.

Lisp에는 또한 목록이 있습니다. "car"및 "cdr"함수를 사용하여 각각 목록의 첫 번째 요소 또는 나머지 목록을 가져올 수 있습니다.

이제 목록의 첫 번째 요소를 새 값으로 바꾸려면 어떻게해야합니까? 저것을하기를 위해 표준 기능이 있고, 믿을 수 없, 그것의 이름은 "차"보다는 더 나쁘다. 그것은 "rplaca"입니다. 그러나 "rplaca"를 기억할 필요는 없습니다.

  (setf (car somelist) whatever)

somelist의 자동차를 설정합니다.

여기서 실제로 일어나고있는 것은 "setf"가 매크로라는 것입니다. 컴파일 시간에, 그것의 주장을 조사하고, 첫 번째 것은 형태 (차 SOMETHING)를 가지고 있음을 본다. 그것은 "오, 프로그래머는 무언가의 차를 세우려고 노력하고 있습니다. 그 기능은 'rplaca'입니다." 그리고 다음과 같이 코드를 조용히 다시 작성합니다.

  (rplaca somelist whatever)