Macros 在宏中使用外部(词汇)环境中的变量
如何使此宏按预期运行?--我想从词法环境中捕获p,而不必将其作为参数发送到宏Macros 在宏中使用外部(词汇)环境中的变量,macros,scheme,lisp,common-lisp,Macros,Scheme,Lisp,Common Lisp,如何使此宏按预期运行?--我想从词法环境中捕获p,而不必将其作为参数发送到宏 (define-syntax-rule (fi a b) (if p a b)) ;--->capture `p` from lexical env (let ((p #t)) (fi 1 2)) 额外的感谢--我如何在CL中做同样的事情?您不能用语法规则捕获本地绑定。您可以使用语法大小写,但是: (define-syntax fi (lambda (stx) (syntax-ca
(define-syntax-rule (fi a b)
(if p a b)) ;--->capture `p` from lexical env
(let ((p #t))
(fi 1 2))
额外的感谢--我如何在CL中做同样的事情?您不能用
语法规则捕获本地绑定。您可以使用语法大小写
,但是:
(define-syntax fi
(lambda (stx)
(syntax-case stx ()
((_ a b)
(with-syntax ((p (datum->syntax stx #'p)))
#'(if p a b))))))
然而,使用datum->syntax
来捕获像这样的固定名称的标识符并不理想。如果您使用的是Racket,最好使用语法参数
对于没有语法大小写但具有显式重命名的方案实现,可以这样编写宏:
(define-syntax fi
(er-macro-transformer
(lambda (exp rename compare)
`(,(rename 'if) p ,(cadr exp) ,(caddr exp)))))
有些人觉得这很简单,但是你有责任重新命名你不是有意捕获的东西。在本例中,我们显式地重命名了if
;对于大多数其他使用lambda
、let
等的宏,这些宏都必须重命名。在公共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的调用
您给出的示例很简单,因为参数的计算结果是独立的。如果有更复杂的表达式(*11)和(+11),则会传入实际的列表结构(a的值是列表(*11),b的值是列表(+1))
问题不是关于Common Lisp,而是Scheme。@Sylvester从“奖金感谢——我如何在CL中做同样的事情?”的问题中回答道,“我的错,没有听清楚。”。在这种情况下,它需要一个标签:)谢谢克里斯!我将对此进行一次尝试,但出于好奇,这里没有任何方法可以实现defmacro那样的简单性吗?@rebnoob大多数Scheme方言都提供define macro,这几乎是一样的。正如您所说,datum->syntax的这种用法是有问题的。如果在宏的词法范围内绑定了相同的外部名称,并且在调用站点本地绑定了相同的外部名称(比如使用let表达式),则它将选择其中的第一个名称。定义宏将做相反的事情,这至少是一个更可预测的破坏卫生!我应该坚持定义宏(如果可用),或者语法参数(如果可用)。@rebnoob在Scheme中使用类似defmacro的东西的问题是,捕获其他东西太容易了。例如,在该宏的特定情况下,如果在当前词汇上下文中重新定义了,该怎么办?(Common Lisp通过使用符号包来解决这个问题,并确保Common-Lisp
包中的东西不能被重新定义。Scheme没有这样的概念。)话虽如此,如果您想要“简单性”,另一种方法是编写显式重命名宏。我将用这样一个实现更新我的帖子。@malisperdefine macro
在几乎任何提供语法规则以外内容的Scheme系统上都很容易定义。然而,define macro
的问题在于它没有任何方法来限制卫生的缺乏。即使是GenSym也无法应对这样一个事实,即当前宏中的所有“免费”标识符(如if
,或仅是lambda
或let
)都可以在当前词汇上下文中反弹。
(fi (* 1 1) (+ 1 1)) => (if p (* 1 1) (+ 1 1))