如何让这块宏按预期运行? - 我想从词汇环境中捕获p,而不必将其作为参数发送给宏。
(define-syntax-rule (fi a b)
(if p a b)) ;--->capture `p` from lexical env
(let ((p #t))
(fi 1 2))
奖金谢谢 - 我如何在CL中做同样的事情?
答案 0 :(得分:2)
在Common Lisp中,一个宏只是一个函数,它将代码的列表结构作为输入,并返回表示新代码的列表结构。
(defmacro fi (a b)
`(if p ,a ,b))
所以,如果你像这样使用fi:
(let ((p t)) ; Common Lisp uses 't' for truth.
(fi 1 2))
就好像你输入了一样:
(let ((p t))
(if p 1 2))
要想看看如何进行这种扩展,想象fi是一个函数,你给它的参数为1和2。
(fi 1 2) => (if p 1 2)
然后获取它返回的列表结构,并用对fi的调用替换它。
您给出的示例很简单,因为参数可以自我评估。如果你有像表达式(* 1 1)和(+ 1 1)那样更复杂的东西,则传入实际的列表结构(a的值是列表(* 1 1),b的值是列表(+ 1 1))
(fi (* 1 1) (+ 1 1)) => (if p (* 1 1) (+ 1 1))
答案 1 :(得分:1)
您无法使用syntax-rules
捕获本地绑定。不过,您可以使用syntax-case
:
(define-syntax fi
(lambda (stx)
(syntax-case stx ()
((_ a b)
(with-syntax ((p (datum->syntax stx #'p)))
#'(if p a b))))))
但是,使用datum->syntax
来捕获像这样的固定名称的标识符并不理想。如果你正在使用Racket,最好使用语法参数。
对于没有syntax-case
但显式重命名的Scheme实现,您可以这样编写宏:
(define-syntax fi
(er-macro-transformer
(lambda (exp rename compare)
`(,(rename 'if) p ,(cadr exp) ,(caddr exp)))))
有些人发现它更简单,但是您有责任重命名您未有意捕获的所有内容。在这种情况下,我们会明确重命名if
;对于使用lambda
,let
等的大多数其他宏,必须重命名这些宏。