闭包和动态作用域?

9

我认为我理解为什么在使用动态作用域的语言中允许闭包存在是危险的。也就是说,似乎你可以正确地关闭变量,但是当尝试读取它时,你只会得到全局堆栈顶部的值。如果其他函数在期间使用相同的名称,则可能很危险。

我是否忽略了其他微妙之处?

2个回答

9
我意识到我回答这个问题已经晚了几年,但是当我通过网络搜索找到这个问题时,我想更正一些错误信息。
“闭包”只是一个可调用对象,其中包含代码和提供该代码中自由变量绑定的环境。该环境通常是词法环境,但技术上它也可以是动态环境。
关键是在环境中关闭代码而不是特定值。这就是Lisp 1.5所做的,也是MACLisp为“向下funargs”所做的。
您可以通过阅读http://www.softwarepreservation.org/projects/LISP/book中的Lisp 1.5手册来了解Lisp 1.5如何执行此操作。
请特别注意附录B中eval如何处理FUNCTION以及apply如何处理FUNARG。
您可以从http://c2.com/cgi/wiki?DynamicClosure中了解使用动态闭包进行编程的基本风格。
你可以从ftp://publications.ai.mit.edu/ai-publications/pdf/AIM-199.pdf中深入了解实现问题。
现代动态作用域语言通常使用浅绑定,其中每个变量的当前值保存在一个全局位置,并且函数调用将旧值保存在堆栈上。使用浅绑定实现动态闭包的一种方法在http://www.pipeline.com/~hbaker1/ShallowBinding.html中描述。

我意识到我可能晚了几年才问这个问题:P,但您介意解释一下make-adderaddxdo-test是什么吗?从Lisp 1.5手册来看,它们不是函数闭包,尽管使用了lambda进行定义。它们似乎更像宏,并且被简单的表达式替换(即没有关联环境)。 - wlnirvana
它们只是普通的动态作用域函数。当被调用时,它们可以访问其动态范围内的变量。在此程序中,唯一的闭包/函数参考是由 FUNCTION 创建的。 - Andru Luvisi
好的,也许这只是一个命名问题,但我更喜欢说没有所谓的“动态闭包”(我个人认为这比澄清更令人困惑)。引用的AIM-199.pdf文件实际上给出了闭包一词的起源,表明它最初仅用于“封闭”的lambda表达式。在这个意义上,那些自由变量逃逸到当前运行时环境的动态作用域函数并不是封闭的,因此在该论文中被称为“开放的lambda表达式”。 - wlnirvana

7
是的,这是基本问题。然而,“closure”一词其实是“lexical closure”的缩写,根据定义捕获了它的词法范围。我会称一个动态作用域语言中的东西为其他名称,比如LAMBDA。只要不尝试返回它们,在动态作用域语言中lambda是完全安全的。
(对于一个有趣的思想实验,将在Emacs Lisp中返回动态作用域lambda的问题与在C中返回指向堆栈分配变量的引用的问题进行比较,并且两者都在Scheme中是不可能的。)
很久以前,在拥有动态作用域的语言比今天更少的时候,这被称为funargs问题。你提到的问题是upward funargs问题。

4
我不会编辑答案,但是:“通常”这个词是错误的——闭包始终是“词法闭包”,因为它们关闭其词法环境中的表达式。至于lambda函数是否安全——无法将lambda函数用作闭包会严重降低它们的价值,但即使没有这一点,动态作用域对程序的健康也是内在的不利影响,因为您无法确定任何绑定的含义。 - Eli Barzilay
谢谢您提供Funargs链接,我之前从未了解过。 - Eli Schneider
@Eli Barzilay:你说得对,我表达得过于含糊。我会删除“通常”的字眼。至于程序的整体健康状况,我完全同意,但问题并不是一般性的。就像我所说的,动态作用域的lambda表达式和无限制的指针一样安全... - Nathan Shively-Sanders
@Eli:你可能从未听说过funargs问题的原因是它相当古老,现在几乎所有的语言都使用词法作用域来解决它。(还有其他原因。) - Nathan Shively-Sanders

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接