以下代码有效:
(define foo (lambda (x)
(lambda (y) (+ x y))))
((foo 2) 5) ; => 7
但是如果我想编写一个为我定义lambda的宏,其中包含任意数量的参数,如下所示:
(define-syntax create-lambda
(syntax-rules ()
((_ name args)
(define name (lambda args
(lambda (y) (+ x y)))))))
并像这样使用它:
;; create the lambda named "foo" with arguments "(x)"
(create-lambda foo (x))
((foo 2) 5) ; => Unbound variable: x
我收到一个未绑定的变量错误 我知道 x 未定义,因为它包含在我的宏中的 args 中。
但如果我显示我的宏的结果,我得到:
(define foo (lambda (x) (lambda (y) (+ x y))))
这对我来说非常有用。
为什么将lambda参数定义为单个变量 args 会阻止我绑定内部的内容?
答案 0 :(得分:2)
Scheme中的宏是卫生的。因此,如果您传递宏x
,则它与宏中的变量x
不同,因为它们来自不同的范围。一个有用的实际例子是swap!
宏:
(define-syntax swap!
(syntax-rules ()
((_ from to)
(let ((tmp from))
(set! from to)
(set! to tmp)))))
(define tmp 10)
(define test 20)
(swap! tmp test)
(list tmp test) ; ==> (20 10)
宏扩展器的天真版本可能会泄漏使用变量tmp
的宏并最终为(20 20)
,但(swap! tmp test)
的扩展可能看起来像这样:
(let ((tmp$1 tmp))
(set! tmp test)
(set! test tmp$1)))))
我只是将1美元附加到宏创建的符号上。它可能最终看起来一样,但对于Scheme实现,它们是不同的变量,就像它们有不同的名称一样。宏不应该依赖于没有得到冲突的符号,因为程序员倾向于重用相同的符号。你的代码看起来像:
(define name
(lambda (x)
(lambda (y$1) (+ x$1 $1)))))))
显然x$1
未绑定且不存在。无论如何,你宁愿对一个名称和正文都作为输入的宏感兴趣,比如:
(define-syntax lambda-arity1
(syntax-rules ()
; one argument
((_ (arg) body ...)
(lambda (arg) body ...))
; two or more arguments
((_ (arg arg2 ...) body ...)
(lambda (arg) (lambda-arity1 (arg2 ...) body ...)))))
(lambda-arity1 (a b c d) (+ a b c d))
; == (lambda (a) (lambda (b) (lambda (c) (lambda (d) (+ a b c d)))))
由于名称也来自列表中的用户a
,因此它与正文中的a
相同,并且完美无缺。 (((lambda-arity1 (a b) (+ a b) 1) 2) ; ==> 3