30

所以......我是scheme r6rs的新手,并且正在学习宏。有人可以向我解释“卫生”是什么意思吗?

提前致谢。

4

6 回答 6

26

卫生通常用于宏的上下文中。卫生宏不使用可能会干扰扩展代码的变量名称。这是一个例子。假设我们想or用宏定义特殊形式。直觉上,

(or a b c ... d)会扩展到类似(let ((tmp a)) (if tmp a (or b c ... d))). (为简单起见,我省略了空壳(or)。)

现在,如果该名称tmp实际上是在代码中添加的,就像上面的扩展草图一样,它会不卫生,而且很糟糕,因为它可能会干扰另一个具有相同名称的变量。说,我们想评估

(let ((tmp 1)) (or #f tmp))

使用我们直观的扩展,这将成为

(let ((tmp 1)) (let ((tmp #f)) (if tmp (or tmp)))

tmp宏中的 遮蔽了最外层的,tmp因此结果是#f代替1

现在,如果宏是卫生的(并且在 Scheme 中,使用 时会自动出现这种情况syntax-rules),那么tmp您将使用一个保证不会出现在代码中其他任何地方的符号,而不是使用扩展的名称。您可以gensym在 Common Lisp 中使用。

Paul Graham 的 On Lisp有关于宏的高级材料。

于 2010-06-09T22:56:31.460 回答
9

如果您想象一个宏只是简单地扩展到使用它的地方,那么您也可以想象如果您在宏中使用一个变量,那么在使用该a宏的地方可能已经定义了一个变量a

不是a你想要的!

这样的事情不可能发生的宏观系统称为卫生系统。

有几种方法可以解决这个问题。一种方法是简单地在宏中使用非常长、非常神秘、非常不可预测的变量名。

一个稍微更精细的版本是gensym其他一些宏系统使用的方法:代替you,程序员提出一个非常长、非常神秘、非常不可预测的变量名,你可以调用gensym生成一个非常长、非常为您提供神秘、非常不可预测且唯一的变量名称。

就像我说的,在一个卫生的宏观系统中,这样的冲突一开始就不会发生。如何使宏观系统卫生本身就是一个有趣的问题,Scheme 社区已经在这个问题上花费了几十年的时间,并且他们不断提出越来越好的方法来做到这一点。

于 2010-06-09T22:53:38.040 回答
3

我很高兴知道这种语言仍在使用!卫生代码是在注入(通过宏)时不会与现有变量发生冲突的代码。

维基百科上有很多关于此的好信息:http ://en.wikipedia.org/wiki/Hygienic_macro

于 2010-06-09T22:55:23.117 回答
2

这是我发现的。解释它的含义完全是另一回事!

http://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-ZH-1.html#node_toc_node_sec_12.1

于 2010-06-09T22:53:49.227 回答
2

宏转换代码:它们将一段代码转换为其他代码。作为该转换的一部分,他们可能会用更多代码围绕该代码。如果原始代码引用了一个变量a,并且在它周围添加的代码定义了一个新版本的a,那么原始代码将无法按预期工作,因为它将访问错误a:如果

(myfunc a)

是原始代码,期望a为整数,宏将X其转换为

(let ((a nil)) X)

然后宏将适用于

(myfunc b)

(myfunc a)会变成

(let ((a nil)) (myfunc a))

这不起作用,因为myfunc将应用于nil而不是它所期望的整数。

卫生宏通过确保使用的名称是唯一的,避免了访问错误变量的问题(以及相反的类似问题)。

维基百科对卫生宏有很好的解释。

于 2010-06-09T22:59:19.620 回答
2

除了提到的所有内容之外,Scheme 的卫生宏还有一件重要的事情,它来自词法范围。

假设我们有:

(syntax-rules () ((_ a b) (+ a b)))

作为宏的一部分,它肯定会插入+,当已经存在+时它也会插入它,但随后是另一个与 具有相同含义的符号+。它将符号绑定到它们在syntax-rules谎言的词法环境中的值,而不是它应用的地方,毕竟我们是词法范围的。它很可能会在此处插入一个全新的符号,但该符号全局绑定到与+定义宏的位置相同的含义。当我们使用如下结构时,这是最方便的:

(let ((+ *))
  ; piece of code that is transformed
)

因此,宏的编写者或用户不必忙于确保它的使用顺利进行。

于 2010-06-10T22:33:49.730 回答