【问题标题】:What are the practical differences between special forms and macros?特殊形式和宏之间的实际区别是什么?
【发布时间】:2012-03-12 21:33:26
【问题描述】:

特殊形式和宏之间有什么实际区别吗?它们有什么不同?

【问题讨论】:

标签: macros clojure lisp scheme common-lisp


【解决方案1】:

与特殊形式相比,宏形式可以进行宏扩展:

CL-USER(1): (macroexpand '(with-slots (x y z)
                              foo
                            (format t "~&X = ~A" x)))

(LET ((#:G925 FOO))
  (DECLARE (IGNORABLE #:G925))
  (DECLARE (SB-PCL::%VARIABLE-REBINDING #:G925 FOO))
  #:G925
  (SYMBOL-MACROLET ((X (SLOT-VALUE #:G925 'X))
                    (Y (SLOT-VALUE #:G925 'Y))
                    (Z (SLOT-VALUE #:G925 'Z)))
    (FORMAT T "~&X = ~A" X)))
T

【讨论】:

  • 嗨,马蒂亚斯。在 tAoAI Norvig 声称setf 是一种特殊形式。但是它确实进行了宏扩展。例如:(macroexpand '(setf test 4)) 扩展为(SETQ TEST 4) 可能书中有错误?我在这里也没有看到setflispworks.com/documentation/lw60/CLHS/Body/…
【解决方案2】:

Lisp 具有某些语言原语,它们构成了 Lisp 形式:

  • 文字数据:数字、字符串、结构......
  • 函数调用,如(sin 2.1)((lambda (a b) (+ a b 2)) 3 4)
  • 特殊运算符用于特殊形式。这些是原始的内置语言元素。请参阅 Common Lisp 中的 Special Operators。这些需要在解释器和编译器中实现。 Common Lisp 无法让开发人员引入新的特殊运算符或提供您自己的版本。代码解析工具需要理解这些特殊运算符;这些工具在 Lisp 社区中通常被称为“code walkers”。在定义 Common Lisp 标准期间,确保数量非常少,并且所有扩展都通过新函数和新宏完成。
  • :宏是转换源代码的函数。转换将递归进行,直到源代码中没有宏。 Common Lisp 具有内置宏,允许用户编写新宏。

所以特殊形式和宏之间最重要的实际区别是:特殊运算符是内置的语法和语义。它们不能由开发人员编写。宏可以由开发者编写。

【讨论】:

    【解决方案3】:

    这些术语不是完全同义的,但它们也不是排他性的(这个答案假设 Scheme):

    • 特殊形式(在方案报告中也称为语法)是不根据函数应用程序的默认规则评估的表达式。 (为了明确起见,默认规则是 eval 所有子表达式,然后 apply 第一个结果到其他结果列表。)
    • 宏系统是一种语言功能,允许在语言本身内定义新的特殊形式。 是使用宏系统定义的特殊形式。

    所以你可以说“特殊形式”是一个属于接口语义的术语,而“宏”是一个属于实现的术语。 “特殊形式”的意思是“这些表达式是用特殊的规则来计算的”,而“宏”的意思是“这是一个特殊规则的实现,用于计算某些表达式。”

    现在重要的一点是,大多数 Scheme 特殊形式都可以定义为来自非常小的原语核心的宏:lambdaif 和宏。仅提供这些的最小 Scheme 实现仍然可以将其余部分实现为宏;最近的方案报告通过引用可以根据宏定义的“库语法”等特殊形式进行了区分。然而,在实践中,实际的 Scheme 系统通常会实现一组更丰富的表单作为原语。

    从语义上讲,表达式唯一重要的是使用什么规则来评估它,而不是该规则是如何实现的。所以从这个意义上说,特殊形式是实现为宏还是原语并不重要。但另一方面,Scheme 系统的实现细节经常“泄露”,所以你可能会发现自己很在意它......

    【讨论】:

    • 您也可以将if 实现为lambda 之上的宏。它的效率不是很高,但知道它是一件好事。
    • 我觉得这个答案是最清楚的。因为它根据其语义定义了special-form——也就是说——不依赖于实现细节。
    • @amalloy,我对如何做到这一点很感兴趣,你手边有链接吗?
    • @SimonLindgren 我可能夸大了这里的情况,因为我认为你必须重建很多语言才能使用更多的 lambda。但是codon.com/programming-with-nothing 是一篇关于 Ruby 中的 lambda 演算的精彩博文,refheap.com/paste/5073/fullscreen 是 Clojure 翻译。由此,您可以定义一个宏,让您“实际上”只使用 lambda,同时仍将漂亮的 if 作为语法糖。
    【解决方案4】:

    对我来说,最实际的区别在于调试器:宏不会出现在调试器中;相反,宏扩展中的(通常)晦涩的代码显示在调试器中。调试此类代码确实很痛苦,并且有充分的理由在开始依赖宏之前确保它们坚如磐石。

    【讨论】:

    • 如果你没有宏调试器,这是一个问题,但有些 Lisps 确实有。例如,Racket 有一个您甚至可以从REPL 使用。
    【解决方案5】:

    懒人的超级简短答案

    您可以随时编写自己的宏,但如果不重新编译 clojure,则无法添加特殊形式

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-08
      • 2021-06-12
      • 1970-01-01
      • 2010-10-22
      • 1970-01-01
      • 2012-05-08
      • 2011-03-06
      • 2020-02-16
      相关资源
      最近更新 更多