当前位置:  开发笔记 > 编程语言 > 正文

Clojure什么时候检查复发是否出现在尾部位置?

如何解决《Clojure什么时候检查复发是否出现在尾部位置?》经验,为你挑选了1个好方法。

我试图了解Clojure对recur非尾部位置的保护是如何起作用的.

如果编写类似的代码,Clojure会抛出异常:

(def some_var (recur))

但是,如果我评估动态创建的代码呢?

(def code '(recur))
(def some_var (eval code))

如果您尝试在REPL中运行此代码,它似乎无限循环.我预计它会抛出异常.

我的问题:

当Clojure确实检查复发是否处于尾部位置时?

我的第二个代码示例的确切语义是什么(动态执行的非尾部位置的重复)?



1> Michał Marcz..:

观察到的行为的解释(无限循环)

您的eval调用实际上会导致编译尾部位置recur 确实发生的代码.

这是因为如何eval实现 - 如果你传递一个evalClojure持久集合的表单,但它看起来不像一个def表单,它将被包装在一个fn表单中,该fn表单是实际编译的,然后是结果函数被调用.

以下是这适用于您的示例:

(eval '(recur))

;; does '(recur) look like a def form?
;; ? no, so transform the above, in effect, to
((eval '(fn [] (recur)))

;; more precisely, before handing off `'(recur)` to lower-level
;; compilation methods, wrap it in `(fn [] …)`:
(fn [] (recur))

;; then immediately call the resulting function with no arguments

;; ultimate result: loop endlessly

如果你想看看这发生了什么,请看一下public static Object eval(Object form, boolean freshLoader)方法clojure.lang.Compiler- 链接到Clojure 1.8的代码.

请注意,出于同样的原因,当前输入(recur)内置REPL(从1.9.0-alpha14开始)也会无休止地循环.不同的REPL实现可能会或可能不会预先处理输入表单,以防止这种情况发生之前eval.

语义学 recur

这些与Alex Miller在他的回答和评论中所关联的官方文档中所解释的完全相同.总而言之,recur必须在建立recur目标的表格内使用; loop,fnreify(方法实现内部)是这样的形式的所有实施例.

如何强制执行语义

上面的语义是在编译时通过使用少量动态Vars来强制执行的,当编译器下载到为编译传递的顶级表单的各种子表单时,编译器会对其进行适当的绑定.如果你想跟着详细的控制流程,搜索的用途NO_RECUR,LOOP_LABELLOOP_LOCALS在Compiler.java.它的要点是,如果表单不在尾部位置,这些Vars将绑定到表示编译时就是这种情况的值.

尽管ClojureScript基于相同的基本思想,但它使用的设置可能更容易理解.请参阅analyzer.clj(使用v1.9标记的稳定链接); 特别是*recur-frames*disallowing-recur.

推荐阅读
爱唱歌的郭少文_
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有