Macros 特殊形式和宏之间的实际区别是什么?
特殊形式和宏之间有实际区别吗?它们有什么不同?与特殊表单相比,宏表单可以进行宏扩展:Macros 特殊形式和宏之间的实际区别是什么?,macros,clojure,lisp,scheme,common-lisp,Macros,Clojure,Lisp,Scheme,Common Lisp,特殊形式和宏之间有实际区别吗?它们有什么不同?与特殊表单相比,宏表单可以进行宏扩展: CL-USER(1): (macroexpand '(with-slots (x y z) foo (format t "~&X = ~A" x))) (LET ((#:G925 FOO)) (DECLARE (IGNORABLE #:G925)) (DECLARE (SB-PC
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
Lisp具有某些语言原语,它们构成了Lisp表单:
- 文字数据:数字、字符串、结构
- 函数调用,如
或类似(sin2.1)
((lambda(ab)(+ab2))34)
- 特殊运算符用于特殊形式。这些是基本的内置语言元素。请参见Common Lisp中的。这些需要在解释器和编译器中实现。CommonLisp无法让开发人员引入新的特殊操作符,也无法提供自己的特殊操作符版本。代码解析工具需要理解这些特殊操作符;在Lisp社区中,这些工具通常被称为“代码漫游器”。在定义通用Lisp标准的过程中,确保了该数字非常小,并且所有扩展都是通过新函数和新宏完成的
- 宏:宏是转换源代码的函数。转换将递归进行,直到源代码中没有宏。Common Lisp具有内置宏,允许用户编写新宏
因此,特殊形式和宏之间最重要的实际区别在于:特殊运算符是内置的语法和语义。它们不能由开发人员编写。宏可以由开发人员编写。这些术语并不是同义词,但它们也不是排他性的(此答案假设方案):
- 特殊形式(在方案报告中也称为语法)是一种未根据函数应用的默认规则进行计算的表达式。(为了明确起见,默认规则是
所有子表达式,然后eval
将第一个子表达式的结果应用到其他子表达式的结果列表中。)apply
- 宏系统是一种语言功能,允许在语言本身中定义新的特殊形式。宏是使用宏系统定义的特殊形式
lambda
、if
和宏。一个只提供这些的最小方案实现仍然可以将其余的作为宏实现;最近的Scheme报告通过引用诸如“库语法”之类的特殊形式(可以用宏来定义)做出了区分。然而,在实践中,实际的Scheme系统通常实现一组更丰富的表单作为原语
从语义上讲,表达式唯一重要的是使用什么规则对其求值,而不是如何实现该规则。因此,从这个意义上讲,特殊形式是作为宏还是原语实现并不重要。但另一方面,Scheme系统的实现细节经常“泄漏”,因此您可能会发现自己关心它……对我来说,最实际的区别在于调试器:宏不会出现在调试器中;相反,(通常)隐藏宏扩展中的代码显示在调试器中。调试这样的代码是一件非常痛苦的事情,在你开始依赖宏之前,确保它们坚如磐石是一个很好的理由。这是懒人的超短答案
您可以在任何时候编写自己的宏,尽管您不能在不重新编译clojure的情况下添加特殊表单 如果
if
也可以作为lambda
上的宏实现的可能副本。这不是非常有效,但知道这一点很简单。我觉得这个答案是最清楚的。由于它根据语义定义了特殊形式
,也就是说,不依赖于实现细节。@amalloy,我对如何做到这一点很感兴趣,你手头有链接吗?@SimonLindgren我可能在这里夸大了这种情况,因为我认为你必须重新构建大量语言才能使用更多的lambda。但这是一篇关于Ruby中lambda演算的精彩博文,是Clojure的翻译。由此,您可以定义一个宏,让您“实际上”只使用lambda,同时仍然使用漂亮的if
作为语法糖。如果您没有宏调试器,这是一个问题,但一些Lisp确实有。例如,Racket有一个你甚至可以从.Hi Matthias使用的。在陶艾岛,诺维格声称,setf
是一种特殊形式。然而,它确实在扩大。例如:(宏扩展)(setf测试4))
扩展到(SETQ测试4)
书中可能有错误?我在这里也看不到setf
: