1

我最近正在学习方案并且对没有标识符就无法评估宏的设计感到好奇,而 lambda(procedure) 可以这样做。

例如,我可以像这样使用匿名 lambda:

((lambda x x) 1 2 3)

似乎我必须使用以下语法定义一个宏:

(define-macro my-macro (lambda x x))

我很好奇为什么会有这样的方法直接像这样创建一个宏:

(define my-macro (macro-lambda x x))

我想如果将宏和 lambda 视为不同的类型,第二个可能会更优雅。

我的问题是:

  1. define-macro什么?
  2. 宏和 lambda 之间的根本区别是什么?
  3. 方案中是否有匿名宏,如果没有,为什么?
4

2 回答 2

5

首先是如何模拟“匿名宏”的示例。然后我将评论有用性。

考虑这个匿名宏转换器,它接受一个表示数字的语法对象并返回一个表示输入数字的双倍的新语法对象。

(λ (stx) 
  (datum->syntax stx
    (* 2 (syntax->datum stx))))

我们可以这样测试:

> ((λ (stx) 
    (datum->syntax stx
      (* 2 (syntax->datum stx))))
  #'3)

结果是一个带有 6 的语法对象。

如果我们想要实际的数字 6,我们可以使用eval.

> (eval ((λ (stx) 
          (datum->syntax stx
            (* 2 (syntax->datum stx))))
        #'3))
6

现在我们使用匿名语法转换器将 3 重写为 6。

匿名宏有用吗?

匿名函数用在你函数的地方,但你只需要一次。如果同一个匿名函数在一个程序中使用了两次,最好给它一个名字,然后引用它两次。

语法转换也是如此。如果您至少需要两次相同的转换,则应为其命名。唯一有意义的匿名语法转换是,如果它只使用一次。在大多数情况下,仅编写转换结果会更简单。我想不出一个例子,匿名转换会使事情变得更简单。

注意:所有示例均在 Racket 中进行了测试。

于 2013-02-03T13:14:16.213 回答
0

宏在编译时转换树,而其余代码在运行时执行。

通常,这两种操作(宏扩展和 lisp 代码执行)都是处理列表的过程。即,对于宏和过程而言,lambda 没有根本区别,只是它们执行的时间。这意味着会出现一些其他问题,例如命名等。

(define ..) 在运行时执行,所以我认为你的建议不会真正起作用。

所以 defmacro 语法清楚地表明宏是一个普通函数,但在编译时进行评估。宏 lambda 宁愿掩盖这一点。

于 2013-02-03T12:24:20.810 回答