【问题标题】:Using (lambda args ()) prevents me from binding what's inside args使用 (lambda args ()) 可以防止我绑定 args 中的内容
【发布时间】:2017-09-06 20:37:48
【问题描述】:

以下代码有效:

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

【问题讨论】:

    标签: lambda macros scheme


    【解决方案1】:

    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

    【讨论】:

    • 如果我理解得很好,这就是使 Scheme 成为“词法范围”语言的原因。感谢您的回答和示例!
    • @Jérôme 甚至编译时代码在 Scheme 中也是词法范围的。在 CL 中,这也是一种词法范围的语言,宏系统不提供卫生,因此它要求程序员使用包或 gensym 来避免变量冲突。
    猜你喜欢
    • 2013-08-04
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 2017-03-26
    • 1970-01-01
    • 2011-02-19
    • 1970-01-01
    • 2022-06-17
    相关资源
    最近更新 更多