Lisp 매크로를 그렇게 특별하게 만드는 것은 무엇입니까?


297

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

또한 파이썬, Java, C # 또는 C 개발의 세계에서 이해할 수있는 것과 관련이 있습니다.


2
그런데 C # 용 LISP 스타일 매크로 프로세서 인 LeMP가 있습니다 : ecsharp.net/lemp ... JavaScript에는 Sweet.js라는 것도 있습니다 : sweetjs.org
Qwertie

@Qwertie sweetjs는 요즘에도 작동합니까?
fredrik.hjarner

나는 그것을 사용하지 않았지만 가장 최근의 커밋은 6 개월 전에 ... 나에게 충분합니다!
Qwertie

답변:


308

짧은 대답을 제공하기 위해 매크로는 공통 Lisp 또는 DSL (Domain Specific Languages)에 대한 언어 구문 확장을 정의하는 데 사용됩니다. 이러한 언어는 기존 Lisp 코드에 바로 포함됩니다. 이제 DSL은 Lisp와 유사한 구문 (예 : Peter Norvig의 Common Lisp 의 Prolog Interpreter ) 또는 완전히 다른 (예 : Clojure의 Infix Notation Math) 을 가질 수 있습니다 .

좀 더 구체적인 예는 다음과 같습니다.
파이썬에는 언어에 내장 된 목록 이해력이 있습니다. 이것은 일반적인 경우에 대한 간단한 구문을 제공합니다. 라인

divisibleByTwo = [x for x in range(10) if x % 2 == 0]

0에서 9 사이의 모든 짝수를 포함하는 목록을 생성합니다. Python 1.5 에서 그 와 같은 구문은 없었습니다. 다음과 같은 것을 사용하십시오.

divisibleByTwo = []
for x in range( 10 ):
   if x % 2 == 0:
      divisibleByTwo.append( x )

이들은 기능적으로 동일합니다. 불신의 서스펜션을 시작하고 Lisp에 매우 제한된 루프 매크로가 있다고 가정 해 봅시다. 반복을 수행하고 목록 이해에 해당하는 쉬운 방법은 없습니다.

Lisp에서 다음을 작성할 수 있습니다. 이 고안된 예제는 Lisp 코드의 좋은 예제가 아닌 Python 코드와 동일하도록 선택되었습니다.

;; the following two functions just make equivalent of Python's range function
;; you can safely ignore them unless you are running this code
(defun range-helper (x)
  (if (= x 0)
      (list x)
      (cons x (range-helper (- x 1)))))

(defun range (x)
  (reverse (range-helper (- x 1))))

;; equivalent to the python example:
;; define a variable
(defvar divisibleByTwo nil)

;; loop from 0 upto and including 9
(loop for x in (range 10)
   ;; test for divisibility by two
   if (= (mod x 2) 0) 
   ;; append to the list
   do (setq divisibleByTwo (append divisibleByTwo (list x))))

더 나아 가기 전에 매크로가 무엇인지 더 잘 설명해야합니다. 코드에 의해 수행되는 변환입니다. 코드 입니다. 즉, 코드를 인수로 받아들이는 인터프리터 (또는 컴파일러)가 읽은 코드 조각은 결과를 조작하고 결과를 반환하며 그 자리에서 실행됩니다.

물론 그것은 많은 타이핑이고 프로그래머는 게으르다. 그래서리스트 이해를위한 DSL을 정의 할 수있었습니다. 실제로, 우리는 이미 하나의 매크로 (루프 매크로)를 사용하고 있습니다.

Lisp는 몇 가지 특수 구문 형식을 정의합니다. 따옴표 ( ')는 다음 토큰이 리터럴임을 나타냅니다. 준 따옴표 또는 역 따옴표 ( `)는 다음 토큰이 이스케이프 된 리터럴임을 나타냅니다. 이스케이프는 쉼표 연산자로 표시됩니다. 리터럴 '(1 2 3)은 Python과 동일 [1, 2, 3]합니다. 다른 변수에 지정하거나 대신 사용할 수 있습니다. 이전에 정의 된 변수 `(1 2 ,x)인 파이썬의 [1, 2, x]위치 와 동등한 것으로 생각할 수 있습니다 x. 이 목록 표기법은 매크로에 들어가는 마술의 일부입니다. 두 번째 부분은 코드를 지능적으로 대체하는 Lisp 판독기이지만 아래에 가장 잘 설명되어 있습니다.

따라서 lcomp(목록 이해의 줄임말) 이라는 매크로를 정의 할 수 있습니다 . 이 구문은 정확히 우리가 예에서 사용하는 파이썬처럼 될 것입니다 [x for x in range(10) if x % 2 == 0] -(lcomp x for x in (range 10) if (= (% x 2) 0))

(defmacro lcomp (expression for var in list conditional conditional-test)
  ;; create a unique variable name for the result
  (let ((result (gensym)))
    ;; the arguments are really code so we can substitute them 
    ;; store nil in the unique variable name generated above
    `(let ((,result nil))
       ;; var is a variable name
       ;; list is the list literal we are suppose to iterate over
       (loop for ,var in ,list
            ;; conditional is if or unless
            ;; conditional-test is (= (mod x 2) 0) in our examples
            ,conditional ,conditional-test
            ;; and this is the action from the earlier lisp example
            ;; result = result + [x] in python
            do (setq ,result (append ,result (list ,expression))))
           ;; return the result 
       ,result)))

이제 명령 행에서 실행할 수 있습니다 :

CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)

꽤 깔끔하지? 이제 거기서 멈추지 않습니다. 원한다면 메카니즘이나 붓이 있습니다. 원하는 구문을 사용할 수 있습니다. 파이썬이나 C #의 with구문 처럼 . 또는 .NET의 LINQ 구문. 결국 이것이 궁극적 인 유연성 인 사람들을 Lisp로 끌어들이는 것입니다.


51
Lisp에서 목록 이해를 구현 한 +1, 왜 그렇지 않습니까?
ckb

9
: @ckb 실제로 LISP 이미 표준 라이브러리에서 지능형리스트 매크로가 (loop for x from 0 below 10 when (evenp x) collect x), 여기에 더 많은 예제를 . 그러나 실제로 루프는 "단순한 매크로"입니다 (실제로 처음부터 처음부터 다시 구현했습니다 )
Suzanne Dupéron

8
나는 그것이 관련이 없다는 것을 알고 있지만 구문과 구문 분석이 실제로 어떻게 작동하는지 궁금합니다 ... lcomp를 그런 식으로 부르십시오 (세 번째 항목을 "for"에서 "azertyuiop"로 변경) : 범위 10) if (= (% x 2) 0)) 매크로가 여전히 예상대로 작동합니까? 또는 "for"매개 변수가 루프에서 사용되므로 호출 될 때 문자열 "for"여야합니까?
dader

2
@dader 따라서 loop는 실제로 다른 매크로이며 for는 해당 매크로에 사용되는 특수 기호입니다. 루프 매크로없이 매크로를 쉽게 정의 할 수있었습니다. 그러나 내가 가지고 있다면 게시물이 훨씬 길 것입니다. 루프 매크로와 모든 구문은 CLtL에 정의되어 있습니다 .
gte525u

3
다른 언어의 매크로와 혼동되는 한 가지는 매크로가 호스트 언어의 구문에 의해 제한된다는 것입니다. Lispy 매크로는 비 Lispy 구문을 해석 할 수 있습니다. 나는 구문과 같은 하스켈을 작성하고 (Paranthese 없음) Lisp 매크로를 사용하여 해석하는 것을 상상하는 것과 같습니다. 이것이 가능하며, 렉서와 파서를 직접 사용하는 것과 비교하여 매크로를 사용하는 장단점은 무엇입니까?
CMCDragonkai

105

여기 에서 lisp macro에 관한 포괄적 인 토론을 찾을 수 있습니다 .

그 기사의 흥미로운 부분 집합 :

대부분의 프로그래밍 언어에서 구문은 복잡합니다. 매크로는 프로그램 구문을 분해하고 분석 한 후 재 조립해야합니다. 그들은 프로그램의 파서에 액세스 할 수 없으므로 휴리스틱과 베스트 추측에 의존해야합니다. 때로는 절삭 속도 분석이 잘못되어 파손됩니다.

그러나 Lisp는 다릅니다. Lisp 매크로 파서에 액세스 할 수 있으며 실제로는 간단한 파서입니다. Lisp 매크로는 문자열이 아니라 Lisp 프로그램의 소스가 문자열이 아니기 때문에 목록 형식의 소스 코드 조각으로 처리됩니다. 목록입니다. 그리고 Lisp 프로그램은 목록을 분해하고 다시 정리하는 데 능숙합니다. 그들은 매일 이것을 안정적으로 수행합니다.

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

  (setf x whatever)

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

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

이제 목록의 첫 번째 요소를 새 값으로 바꾸려면 어떻게해야합니까? 이를위한 표준 기능이 있으며, 그 이름은 "car"보다 훨씬 나쁩니다. "rplaca"입니다. 하지만 "rplaca"를 기억할 필요는 없습니다.

  (setf (car somelist) whatever)

일부리스트의 자동차를 설정합니다.

여기서 실제로 일어나는 것은 "setf"가 매크로라는 것입니다. 컴파일 타임에 인수를 검사하고 첫 번째 인수가 (car SOMETHING) 형식임을 알 수 있습니다. "아, 프로그래머는 무언가의 차를 세우려고 노력하고있다. 그것을 위해 사용하는 기능은 'rplaca'이다." 그리고 다음과 같이 코드를 조용히 다시 작성합니다.

  (rplaca somelist whatever)

5
setf는 매크로의 힘을 잘 보여줍니다.
Joel

Lisp 프로그램의 소스가 문자열이 아니기 때문에 강조 표시가 마음에 듭니다. 목록입니다. ! LISP 매크로가 괄호로 인해 대부분의 다른 것보다 우수한 이유는 무엇입니까?
학생

내가 그렇게 생각 @Student : books.google.fr/...은 당신이 맞다 제안합니다.
VonC

55

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

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

그러나 리스프 매크로는 코드 스 니펫을 입력으로 사용하고 매크로의 "호출"을 대체하기 위해 코드를 리턴하는 리스프 프로그램이기 때문에 원하는만큼 "기본"레퍼토리를 확장 할 수 있습니다. 더 읽기 쉬운 프로그램으로.

C에서 동일한 작업을 수행하려면 초기 (quite-C가 아닌) 소스를 먹고 C 컴파일러가 이해할 수있는 것을 뱉어내는 커스텀 프리 프로세서를 작성해야합니다. 그것에 대한 잘못된 길은 아니지만 반드시 가장 쉬운 것은 아닙니다.


41

리스프 매크로를 사용하면 어떤 부분이나 표현이 언제 평가 될 것인지 결정할 수 있습니다. 간단한 예를 들어 C를 생각해보십시오.

expr1 && expr2 && expr3 ...

이것이 말하는 것 : 평가 expr1, 그리고 사실이라면 평가 expr2

이제 이것을 &&함수로 만들어보십시오 ... 그렇습니다. 그렇습니다. 다음과 같은 것을 호출 :

and(expr1, expr2, expr3)

거짓 exprs여부 expr1에 관계없이 답을 산출하기 전에 세 가지 를 모두 평가합니다 !

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

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

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

이것이 어떻게 유용한 지 알아 보려면 대조하십시오.

(&& (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%")

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

도움이 되었기를 바랍니다.


"(apply &&, @ exprs); 이것이 잘못되었을 수 있습니다!"
Svante

1
@svante-두 카운트에서 : 첫째, &&는 매크로가 아니라 함수입니다. 함수에만 적용됩니다. 둘째, apply는 전달할 인수 목록을 취하므로 "(funcall fn, @ exprs)", "(apply fn (list, @ exprs)"또는 "(apply fn, @ exprs nil)"중 하나를 원하지 않습니다. "(app fn, @ exprs)"
Aaron

(and ...는 거짓으로 평가 될 때까지 표현식을 평가합니다. 거짓 평가에 의해 생성 된 부작용이 발생한다는 점에 유의하십시오. 후속 표현식 만 건너 뜁니다.
ocodo

31

나는이 동료보다 Lisp 매크로가 더 잘 설명 된 것을 본 적이 없다고 생각한다 : http://www.defmacro.org/ramblings/lisp.html


3
특히 Java / XML 배경 지식이있는 경우.
sunsations

1
토요일 오후에 소파에 누워있는 것을 읽는 것이 얼마나 기쁩니다! 매우 명확하게 작성되고 구성되었습니다.
Colin

10

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

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

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

  • Lisp 매크로는 Lisp로 작성됩니다. 매크로를 작성할 수있는 Lisp의 모든 기능이 있습니다.
  • Lisp는 매우 규칙적인 구문을 가지고 있습니다.

C ++에 능통 한 사람과 대화하고 템플릿 메타 프로그래밍에 필요한 모든 템플릿 퍼지를 배우는 데 걸린 시간을 물어보십시오. 또는 Modern C ++ Design 과 같은 (뛰어난) 책의 모든 미친 트릭 은 언어가 10 년 동안 표준화되었지만 실제 컴파일러 사이에서 디버깅하기가 어렵고 실제로는 이식성이 없습니다. 메타 프로그래밍에 사용하는 언어가 프로그래밍에 사용하는 언어와 동일하면 모든 것이 사라집니다!


11
공평하게 말하면 C ++ 템플릿 메타 프로그래밍의 문제점은 메타 프로그래밍 언어가 다른 것이 아니라 무섭다는 것입니다. 훨씬 간단한 템플릿 기능을 위해 고안된 것만 큼 많이 설계되지는 않았습니다.
Brooks Moses

@ 브룩스 물론입니다. 긴급 기능이 항상 나쁜 것은 아닙니다. 불행히도, 느리게 움직이는위원회 중심 언어에서는 언어를 고치기가 어렵습니다. C ++의 현대적인 유용한 새 기능 중 상당수는 거의 읽을 수없는 언어로 작성되었으며, 평범한 프로그래머와 "대제사장"사이에는 큰 차이가 있습니다.
매트 커티스

2
@ downvoter : 내 대답에 문제가 있으면 의견을 남겨서 모두 지식을 공유하십시오.
매트 커티스

10

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

C #에는 매크로 기능이 없지만 컴파일러가 코드를 CodeDOM 트리로 구문 분석하고이를 메소드로 전달한 다음이를 다른 CodeDOM으로 변환 한 다음 IL로 컴파일하는 경우에도 마찬가지입니다.

for each-statement -clause using, linq -expressions 등과 같은 "sugar"구문을 select기본 코드로 변환하는 매크로 로 구현하는 데 사용할 수 있습니다 .

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

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

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

이제 실제로 존재한다는 것입니다 C #을위한 리스프 스타일 매크로 프로세서는, 나는에 대한 매크로를 지적 할 using같이 )
Qwertie

9

기존의 답변은 매크로가 무엇을 달성하고 어떻게 설명하는지에 대한 구체적인 예를 제공하기 때문에 매크로 기능이 왜 다른 언어와 관련하여 중요한 이익이되는지에 대한 생각을 모으는 데 도움이 될 것 입니다 . 먼저 이러한 답변에서, 다른 곳에서 훌륭한 답변입니다.

... C에서, 당신은 [아마로 자격이 사용자 정의 전처리 작성해야 충분히 복잡한 C 프로그램을 ] ...

C ++에 능통 한 사람과 대화하고 템플릿 메타 프로그래밍을 수행하는 데 필요한 모든 템플릿 퍼지를 배우는 데 얼마나 오래 걸 렸는지 물어보십시오 [여전히 강력하지는 않습니다].

맷 커티스

... Java에서는 바이트 코드 직조로 길을 가야하지만 AspectJ와 같은 일부 프레임 워크에서는 다른 접근법을 사용 하여이 작업을 수행 할 수 있지만 근본적으로 해킹입니다.

미구엘 핑

DOLIST는 Perl의 foreach 또는 Python의 for와 유사합니다. Java는 JSR-201의 일부로 Java 1.5에서 "enhanced"for loop를 사용하여 유사한 종류의 루프 구성을 추가했습니다. 차이점 매크로가 무엇인지 확인하십시오. 코드에서 공통 패턴을 발견 한 Lisp 프로그래머는 매크로를 작성하여 해당 패턴의 소스 레벨 추상화를 제공 할 수 있습니다. 동일한 패턴을 발견 한 Java 프로그래머는 Sun에게이 특정 추상화가 언어에 추가 할 가치가 있음을 확신시켜야합니다. 그런 다음 Sun은 JSR을 게시하고 업계 전체의 "전문가 그룹"을 소집하여 모든 것을 해시해야합니다. Sun에 따르면이 프로세스는 평균 18 개월이 걸립니다. 그 후 컴파일러 작성자는 모두 새로운 기능을 지원하기 위해 컴파일러를 업그레이드해야합니다. Java 프로그래머가 선호하는 컴파일러가 새로운 버전의 Java를 지원하더라도 이전 버전의 Java와의 소스 호환성을 깨기 전에는 새로운 기능을 사용할 수 없을 것입니다. Common Lisp 프로그래머가 5 분 안에 스스로 해결할 수 있다는 성가심은 Java 프로그래머에게 몇 년 동안 전염됩니다.

Peter Seibel, "실제 공통 리스프"


8

모든 사람의 (우수한) 게시물에 통찰력을 추가 할 수 있을지 모르겠지만 ...

Lisp 매크로는 Lisp 구문 특성으로 인해 효과적입니다.

Lisp는 매우 규칙적인 언어입니다 (모든 것이 목록 이라고 생각 하십시오 ). 매크로를 사용하면 데이터와 코드를 동일하게 취급 할 수 있습니다 (리스프 표현식을 수정하는 데 문자열 구문 분석 또는 기타 해킹이 필요하지 않음). 이 두 기능을 결합하면 코드를 매우 깔끔하게 수정할 수 있습니다.

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

따라서 모든 기능을 갖춘 언어 자체를 사용하여 언어 위에 자신의 코드 생성기를 만드는 방법으로 끝납니다 (예 : Java에서는 AspectJ와 같은 일부 프레임 워크를 사용하면 바이트 코드 직조로 길을 열어야합니다) 다른 접근법을 사용 하여이 작업을 수행하십시오 (기본적으로 해킹입니다).

실제로 매크로를 사용하면 추가 언어 나 도구를 배울 필요가 없으며 언어 자체의 모든 기능을 사용하지 않고도 리스프 위에 자체 미니 언어 를 구축 할 수 있습니다.


1
그러나 "모든 것이 목록이다"는 새로운 아이디어를 두려워 할 수 있다는 통찰력있는 의견이다. 목록을 이해하려면 단점, 자동차, cdrs, 셀을 이해해야합니다. 보다 정확하게는 Lisp은 S-expressions목록이 아니라로 구성됩니다.
ribamar

6

리스프 매크로는 거의 모든 규모의 프로그래밍 프로젝트에서 발생하는 패턴을 나타냅니다. 결국 큰 프로그램에는 소스 코드를 텍스트로 출력하여 붙여 넣을 수있는 프로그램을 작성하는 프로그램을 작성하는 것이 더 간단하고 오류가 적다는 것을 인식하는 특정 코드 섹션이 있습니다.

파이썬에서 객체에는 두 가지 메소드 __repr____str__있습니다. __str__단순히 사람이 읽을 수있는 표현입니다. __repr__유효한 파이썬 코드 인 표현, 즉 유효한 파이썬으로 해석기에 입력 할 수있는 표현을 리턴합니다. 이렇게하면 실제 소스에 붙여 넣을 수있는 유효한 코드를 생성하는 작은 Python 스 니펫을 만들 수 있습니다.

Lisp에서이 전체 프로세스는 매크로 시스템에 의해 공식화되었습니다. 물론 구문에 대한 확장을 만들고 모든 종류의 멋진 작업을 수행 할 수는 있지만 실제 유용성은 위와 같이 요약됩니다. 물론 Lisp 매크로 시스템을 사용하면 이러한 "스 니펫"을 전체 언어의 모든 기능으로 조작 할 수 있습니다.


1
첫 번째 단락은 Lisp 외부인에게 매우 명확합니다.
와일드 카드

5

간단히 말해서, 매크로는 코드의 변환입니다. 그것들은 많은 새로운 구문 구성을 도입 할 수 있습니다. 예를 들어 C #에서 LINQ를 고려하십시오. 또한 매크로로 구현되는 유사한 언어 확장이 있습니다 (예 : 내장 루프 구문, 반복). 매크로는 코드 중복을 크게 줄입니다. 매크로는«작은 언어»를 포함 할 수있게합니다 (예 : c # / java에서 xml을 사용하여 구성 할 경우 매크로와 동일한 기능을 수행 할 수 있음). 매크로는 라이브러리 사용의 어려움을 숨길 수 있습니다.

예를 들어, lisp에서 당신은 쓸 수 있습니다

(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를 반복하여 올바르게 닫는 것이 필요합니다.


3

위의 모든 매크로가 무엇인지 설명하고 멋진 예제가 있지만 매크로와 일반 함수의 주요 차이점은 LISP가 함수를 호출하기 전에 모든 매개 변수를 먼저 평가한다는 것입니다. 매크로의 경우 그 반대가됩니다. LISP는 평가되지 않은 매개 변수를 매크로에 전달합니다. 예를 들어, 함수에 (+ 1 2)를 전달하면 함수는 값 3을받습니다.이 값을 매크로에 전달하면 List (+ 1 2)를받습니다. 이것은 매우 유용한 모든 종류의 작업을 수행하는 데 사용할 수 있습니다.

  • 루프 또는 목록의 해체와 같은 새로운 제어 구조 추가
  • 전달 된 함수를 실행하는 데 걸리는 시간을 측정하십시오. 함수를 사용하면 제어가 함수에 전달되기 전에 매개 변수가 평가됩니다. 매크로를 사용하면 스톱워치의 시작과 중지 사이에 코드를 연결할 수 있습니다. 아래는 매크로와 함수에서 정확히 동일한 코드를 가지며 출력은 매우 다릅니다. 참고 : 이것은 고안된 예이며 차이점을 더 잘 강조하기 위해 구현이 선택되었습니다.

    (defmacro working-timer (b) 
      (let (
            (start (get-universal-time))
            (result (eval b))) ;; not splicing here to keep stuff simple
        ((- (get-universal-time) start))))
    
    (defun my-broken-timer (b)
      (let (
            (start (get-universal-time))
            (result (eval b)))    ;; doesn't even need eval
        ((- (get-universal-time) start))))
    
    (working-timer (sleep 10)) => 10
    
    (broken-timer (sleep 10)) => 0

Btw, Scala는 언어에 매크로를 추가했습니다. 언어가 동성애 적이 지 않기 때문에 Lisp 매크로의 아름다움이 부족하지만 확실히 살펴볼 가치가 있으며 결국 제공하는 추상 구문 트리가 더 쉽습니다. 내가 선호하는 매크로 시스템을 말하기가 너무 이릅니다.
Joerg Schmuecker

2
"LISP는 평가되지 않은 매개 변수를 매크로에 전달합니다"라는 최종 답변이 명확하게 나와 있습니다. 그러나 당신은 그것의 후반을 잊었습니다. 매크로의 결과는 원래의 위치 대신 처음부터 있었던 것처럼 시스템에 의해 전체적으로 평가되는 변형 된 코드입니다 (다시 호출하지 않는 한) 이번에 는 해당 매크로에 의해 변형 되는 매크로).
Will Ness

0

나는 일반적인 lisp 요리 책에서 이것을 얻었지만 lisp 매크로가 좋은 방법으로 좋은 이유를 설명했다고 생각합니다.

"매크로는 다른 Lisp 코드에서 작동하는 Lisp 코드의 일반적인 조각으로,이를 Lisp 실행 파일에 더 가까운 버전으로 변환합니다. 약간 복잡한 것처럼 들릴 수 있으므로 간단한 예를 들어 보겠습니다. 두 변수를 같은 값으로 설정하는 setq 버전입니다.

(setq2 x y (+ z 3))

언제 z=8x와 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


1
이 정의 할 수 setq2있다면 함수로서 x그리고 y참조에 의해 전달된다. 그러나 CL에서 가능한지 모르겠습니다. 따라서 Lisps 또는 CL을 모르는 사람에게는 특히 예시가 아닙니다. IMO
neoascetic

@neoascetic CL 인수 전달은 값만입니다 (먼저 매크로가 필요합니다). 일부 값은 목록과 같은 포인터입니다.
Will Ness

-5

파이썬에는 데코레이터가 있으며 기본적으로 다른 함수를 입력으로 사용하는 함수가 있습니다. 함수 호출, 다른 작업 수행, 리소스 호출 릴리스에서 함수 호출 래핑 등 원하는 작업을 수행 할 수 있지만 해당 함수를 들여다 볼 수는 없습니다. 우리가 더 강력하게 만들고 싶다고 가정하면 데코레이터가 함수 코드를 목록으로 받았다면 함수를 그대로 실행할 수있을뿐만 아니라 함수의 일부를 실행하고 함수의 행을 재정렬 할 수 있습니다.

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