2016-08-19 2 views
2

남자.매크로에 '루프 ... 수집'을 쓸 때의 문제

오늘 유연한 표현식 입력의 합계를 계산하기 위해 시그마 매크로를 작성하고 싶습니다.

아래 코드는 오늘 오후에 작성되었습니다. 그러나 그것은 내 목적에 따라 작동하지 않습니다.

(defmacro sigma (exp ll) 
    `(+ ,@(loop for i in ll collect 
      (progn (setf (elt exp 1) i) 
        (print exp) 
        exp))) 
) 

>>(pprint (macroexpand-1 '(sigma (+ 1 2) (2 3 4)))) 
>>(+ 2 2) 
    (+ 3 2) 
    (+ 4 2) 
    (+ (+ 4 2) (+ 4 2) (+ 4 2)) 

가 나는 (+ (+ 2 2) (+ 3 2) (+ 4 2))하지만 loop collect 나에게 이상한 대답을 작동합니다.

왜 이런 식으로 작동합니까? 이 문제를 해결할 수있는 방법이 있습니까?

답변

3

이 방법 다음 copy-list입니다

그가 그것을 돌연변이되지 않은 경우에도
(defmacro sigma ((op arg0 &rest args) ll) 
    (declare (ignore arg0)) 
    `(+ ,@(loop for i in ll collect `(,op ,i ,@args)))) 
6

리터럴 데이터 (따옴표)를 변경하고 있습니다. 해당 루프 동안 (+ 1 2) (exp에 바인딩 된 목록)이 각 반복에서 동일한 하나이며 각 반복에서 두 번째 요소를 변경한다고 동의하면 같은 목록을 수집 한 목록이 exp 3 번이 될 것이라고 상상하기 쉽습니다 3 두 번째 요소의 마지막 돌연변이와 정확히 같은 요소.

이것은 매크로의 기능이 아닙니다. 모든 인용 된 데이터에서 돌연변이를 수행하면 이러한 결과가 발생할 수 있습니다. 표준은 결과가 정의되지 않았으므로 구현자가 아무 문제를 해결할 필요가 없도록하고 특정 구현의 다른 측면에서 예기치 않은 동작을 얻도록 지시합니다.

컴파일 된 파일은 인용 된 모든 데이터를 하나의 코드로 결합하여 '(+ 1 2) 코드의 다른 부분이이 매크로의 영향을받을 수 있습니다.

는 변이하지 않는 간단하게이 문제를 해결하려면 다음

(defmacro sigma ((op r &rest rest) ll) 
    `(+ ,@(loop :for i :in ll 
       :collect (list* op i rest)))) 

(macroexpand-1 '(sigma (+ 1 2) (2 3 4))) 
; ==> (+ (+ 2 2) (+ 3 2) (+ 4 2)) 

이의 좋은 점은 당신이 템플릿에 두 개 이상의 인수를 보장하는 것입니다.

중첩 된 역 인용 부호의 표현도 가능하다 : 당신은 갓은 consed 목록을 원하는 경우

(macroexpand-1 '(sigma (x) (2 3 4))) 
; ==> *** - SIGMA: (X) does not match lambda list element (OP R &REST REST) 
+1

, 그는 루프를 통해 매번 동일한'exp' 오브젝트를 수집하고 있습니다. – Barmar

+0

@Barmar 한 번 'NCONC'-ed는 두 번 크기의리스트를 얻기 위해 두 번 동일한 인수를 기억했습니다 :-) – Sylwester