循环宏中的常见 Lisp 绑定

Posted

技术标签:

【中文标题】循环宏中的常见 Lisp 绑定【英文标题】:Common Lisp Binding in Loop Macro 【发布时间】:2016-01-30 09:20:01 【问题描述】:

我想在循环中重新绑定一个特殊变量。现在,通常这是使用let 完成的。

(let ((*read-eval* nil))
  (do-something-here))

但是由于loop 宏有这些很好的with 子句,我想我可以在那里这样做。表达式(macroexpand '(loop with *read-eval* = nil)) 最终将绑定扩展到let,因此它肯定会专门用于我的实现。但我在the standard 中找不到任何表明这是标准化行为的内容。所以,我想,我的问题是:

(loop with *read-eval* = nil
      for i from 1 to 10
      do (something-involving-the-read-function))

修改现有的*read-eval* 变量是否需要符合要求的实现,或者是否存在创建同名新词法变量的风险?

【问题讨论】:

【参考方案1】:

*read-eval* 是一个全局特殊变量。没有办法撤消它,即为它创建一个本地词法绑定。

with 子句被描述为使用bindings(而不是仅仅设置),这意味着,确实,一旦循环完成,我们将回到原始值(回答@joshua-tailor 的问题)。

让我们理性思考。 (loop with foo = nil ...) 确实为foo 建立了绑定。因此,对于(loop with *read-eval* = nil ...) 建立该绑定,实现必须检查(在宏扩展或编译时)*read-eval* 在运行时是否将是dynamic variable。这听起来很疯狂。

【讨论】:

这是真的,但仍然不清楚 loop 必须重新绑定,而不是仅仅分配。我们是否知道一旦循环完成,我们就会回到原来的值?我很确定答案是肯定的,基于lispworks.com/documentation/HyperSpec/Body/06_abb.htm,它表示变量不再存在于循环之外。措辞对于特殊变量不是特别好,但看起来唯一合理的解释是名称的本地绑定,这意味着 let(或 lambda,等)。

以上是关于循环宏中的常见 Lisp 绑定的主要内容,如果未能解决你的问题,请参考以下文章

Lisp:在宏中扩展属性名称

在宏中循环 3 个操作查询,直到第 4 个选择查询不返回任何记录

在 Clojure 的宏中声明设计模式

如何使用依赖于包装它的较短列表的 map 循环较长的列表以将某些函数应用于 Common Lisp 中的较长列表?

Excel VBA 宏中的数组

类Lisp解释器JavaScript实现