Macros 当我定义一个宏,并且只为一个临时变量使用一个足够罕见的名称时,会出现什么问题?
说: “当然,我们可以想出足够罕见的名字,这样问题就永远不会出现。是的,在许多情况下,包和智能变量命名可以解决变量捕获的问题。然而,最严重的变量捕获错误不会出现在程序员直接创建的代码中。当其他宏以您意想不到的方式使用您的宏(与您的宏组合)时,大多数变量捕获问题只会出现。” 然后它没有给我一个粗体部分的例子。这样的例子会是什么?想象一下一个假想的Lisp开发团队,它的疯狂老板禁止使用gensym或任何创建不感兴趣符号的东西,程序员只是简单地通过掷字母数字骰子来产生随机变量名,比如temp-27s63f8sk2n或temp-27s63f8sk2n当他们错过gensym时,sum-3t84hj4df。团队遇到麻烦的例子是什么Macros 当我定义一个宏,并且只为一个临时变量使用一个足够罕见的名称时,会出现什么问题?,macros,lisp,scope,Macros,Lisp,Scope,说: “当然,我们可以想出足够罕见的名字,这样问题就永远不会出现。是的,在许多情况下,包和智能变量命名可以解决变量捕获的问题。然而,最严重的变量捕获错误不会出现在程序员直接创建的代码中。当其他宏以您意想不到的方式使用您的宏(与您的宏组合)时,大多数变量捕获问题只会出现。” 然后它没有给我一个粗体部分的例子。这样的例子会是什么?想象一下一个假想的Lisp开发团队,它的疯狂老板禁止使用gensym或任何创建不感兴趣符号的东西,程序员只是简单地通过掷字母数字骰子来产生随机变量名,比如temp-27s6
说到这里,Emacs 24.3.1定义了dotimes和dolist,但没有使用不相关的符号。这很奇怪。当您在其他上下文中重复使用自己的宏时,问题就会出现,而您巧妙地使用了名称空间的变量实际上是多余的,因为它们都在同一个名称空间中 我可以想象这样一个例子:使用闭包访问封闭
(let)
中的变量,但将其传递给也使用封闭(let)
定义名称冲突的“安全”变量的宏。这是一个人为的例子,抱歉,我现在想不出实际情况
(defmacro my/a (x)
(let ((my/safe-name x))
`(progn ,(my/b (lambda () my/safe-name))
,my/safe-name)))
(defmacro my/b (f)
`(let ((my/safe-name 4))
(when (evenp (funcall ,f))
(print "F is even!"))))
(my/a 3) ; will print "F is even", but it shouldn't
好吧,那么我建议将“掷字母数字骰子”的过程自动化当然,它不一定是随机的,你可以用一个计数器。此外,如果能够为调试指定一个前缀就更好了。哦,等等,这正是gensym所做的。为什么人们投票关闭这个?这是一个真实而复杂的问题。Gensym返回不感兴趣的符号。假设团队只使用内部符号。如果团队依赖于
intern gensym
,这与gensym
类似,只是它返回了interned符号,那么他们将遇到麻烦,因为名称G10、G11、G12。。。。还不够罕见,而且宏的用户有可能会使用这样的变量名,链接的Let Over Lambda文章中给出了这样一个例子。如果团队使用带有随机后缀的插入符号,这样一个例子的问题是可以避免的。然后这篇文章说还有另一种陷入麻烦的方式,引用中的粗体部分。粗体部分会给团队带来麻烦吗?通常的开发团队会通过两个gensym调用或进行符号调用来编写。假设有问题的开发团队将运行他们的伪随机字母数字后缀生成器两次。因此,它将在my/a和my/b中分别出现((my/safe-name-6kuyd4kq x))和((my/safe-name pqedzkwg 4))。对于这个团队来说,如果遇到麻烦,我们需要扩展代码,以某种方式获取my/safe-name-6kuyd4kq,并使其在某种程度上与自身冲突。我怀疑这就是粗体部分的意思,只是它缺少一个例子,所以我不能确定。使用gensym正是Doug Hoyte建议的解决不必要捕获的方法。只有在忽略使用gensyms时才会出现问题。这就是霍伊特想说的。在书中的这一点上,他假设你还不知道gensyms,只知道手工挑选的(但有名称空间的)变量名。