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=50와 y=-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.