使用(lambda args())可以防止我绑定';args中的s

使用(lambda args())可以防止我绑定';args中的s,lambda,macros,scheme,Lambda,Macros,Scheme,以下代码起作用: (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)))))))

以下代码起作用:

(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会阻止我绑定其中的内容?

Scheme中的宏是健康的。因此,如果您传递一个宏
x
,它与宏中的变量
x
不同,因为它们来自不同的作用域。一个实用的例子是
交换宏:

(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)
,但是
(swap!tmp test)
的扩展可能如下所示:

 (let ((tmp$1 tmp))
   (set! tmp test)
   (set! test tmp$1)))))
我只是在宏创建的符号后面加了$1。可能最终看起来是一样的,但对于方案实现来说,它们是不同的变量,就像它们有不同的名称一样。宏永远不应该依赖于不获取冲突符号,因为程序员倾向于重用相同的符号。您的代码如下所示:

(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
与正文中的
a
相同,并且它工作得非常完美<代码>((lambda-arity1(ab)(+ab)1)2);=>3

方案中的宏是健康的。因此,如果传递一个宏
x
,它与宏中的变量
x
不同,因为它们来自不同的作用域。一个实用的例子是
交换!
宏:

(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)
,但是
(swap!tmp test)
的扩展可能如下所示:

 (let ((tmp$1 tmp))
   (set! tmp test)
   (set! test tmp$1)))))
我只是在宏创建的符号后面加了$1。可能最终看起来是一样的,但对于方案实现,它们是不同的变量,就像它们有不同的名称一样。宏不应该依赖于不获取冲突符号,因为程序员倾向于重用相同的符号。您的代码如下所示:

(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(ab)(+ab)1)2);==>3

如果我理解得很好,这就是方案a“词法范围”的原因语言。谢谢你的anwser和你的例子!@Jérôme即使编译时代码在Scheme中也是词汇范围的。在CL中,这也是一种词汇范围的语言,宏系统不提供卫生,因此它要求程序员使用包或
gensym
来避免变量冲突。如果我理解得很好,这就是Schem的原因e是一种“词汇范围”语言。感谢你的anwser和你的示例!@Jérôme甚至编译时代码在Scheme中也是词汇范围。在CL中,也是一种词汇范围语言,宏系统不提供卫生,因此它要求程序员使用包或
gensym
来避免变量冲突。