Ch 8 끝 부분 인 Practical Common Lisp에서 Peter Seibel은 once-only
매크로를 제공합니다. 이 기능의 목적은 사용자 정의 매크로에서 다양한 평가를 통해 여러 가지 미묘한 문제를 완화하는 것입니다.`once-only` 매크로 사용
(defmacro once-only ((&rest names) &body body)
(let ((gensyms (loop for n in names collect (gensym))))
`(let (,@(loop for g in gensyms collect `(,g (gensym))))
`(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
,@body)))))
다음에 시도 샘플 (잘못된) 인위적인 매크로는 : 난 그냥 그것을 사용하는 방법을 제대로이 매크로 다른 게시물에서와 작품,하지만 방법이 시점에서 이해하려고 노력하고 있지 않다 참고 다양한 변수 평가 문제를 나타냅니다. 이 범위를 반환, 일부 델타에 의해 정수의 범위를 반복하는 취지 :
(defmacro do-range ((var start stop delta) &body body)
"Sample macro with faulty variable evaluations."
`(do ((,var ,start (+ ,var ,delta))
(limit ,stop))
((> ,var limit) (- ,stop ,start))
,@body))
예를 들어, (do-range (i 1 15 3) (format t "~A " i))
이 1 4 7 10 13
를 인쇄 한 후 14
을 반환해야합니다.
문제는 1) 자유 변수로 발생하기 때문에 limit
의 두 번째 어커런스의 잠재적 캡처, 2) 바운드 변수 limit
의 초기 어커런스의 잠재적 캡처는 다른 변수와 함께 표현식에서 발생하기 때문에은 stop
이 매개 변수 목록에 delta
앞에 표시 되더라도 stop
및 start
이 두 번 이상 평가되므로 여러 변수 평가가 수행 되더라도 매크로 평가에서 나타나는 오류가 발생합니다. 내가 알고있는 것처럼, once-only
는 이러한 문제를 해결해야합니다
(defmacro do-range ((var start stop delta) &body body)
(once-only (start stop delta limit)
`(do ((,var ,start (+ ,var ,delta))
(limit ,stop))
((> ,var limit) (- ,stop ,start))
,@body)))
그러나, (macroexpand '(do-range (i 1 15 3) (format t "~A " i)))
에 대한 limit
언 바운드 변수 인 뿌려줍니다. 위에서 with-gensyms
으로 전환하면 1 & 2의 문제 만 처리해야합니다. 확장은 아무런 문제없이 진행됩니다.
once-only
매크로에 문제가 있습니까? 그리고 once-only
은 실제로 위에 설명한 모든 문제 (그리고 아마도 다른 문제)를 실제로 해결합니까?
왜 전혀'limit'을해야합니까 ? 'once-only' 버전에서 모든 사용을', stop'으로 대체 할 수 없었습니까? – melpomene
@melpomene 네, 맞습니다. 그러나 결함이있는 매크로 (위의 문제 1과 2로 나열)에 두 가지 기본 종류 변수 캡처 문제를 도입하는 더 간단한 방법을 생각할 수 없습니다. 다른 아이디어? – davypough