juh*_*ovh 5 stack-overflow recursion clojure
我想用fn生成命名函数并从宏返回它们,我尝试了以下示例:
(defmacro getfn
[namestr children]
`(fn fn-name# []
(println "Recursing" ~namestr)
(doall (map (fn [child#] (child#)) ~children))))
(def foo (getfn "foo" []))
(def bar (getfn "bar" [foo]))
(defn -main [& args]
(bar))
Run Code Online (Sandbox Code Playgroud)
结果输出通常与预期一致:
Recursing bar
Recursing foo
Run Code Online (Sandbox Code Playgroud)
但是,当我运行此编译提前(AOT)时,我得到:
Recursing bar
Recursing bar
...
Recursing bar
Recursing bar
Exception in thread "main" java.lang.StackOverflowError
Run Code Online (Sandbox Code Playgroud)
我发现很奇怪bar不断调用自己而不是foo,唯一合理的原因是生成的符号fn-name#泄漏到其范围之外.这是Clojure中的错误还是预期的行为?
更新:为清楚起见,请注意删除fn-name#符号并使函数匿名修复此问题.但是,在我的实际代码中,我需要有时递归地调用它,因此命名它是必要的.
对于这个问题,我的一个解决方案是使用 gensym 为宏的每个版本获取一个新符号,这可以通过修改 getfn 来实现,如下所示:
(defmacro getfn
[namestr children]
`(let [fn-name# (gensym)]
(fn fn-name# []
(println "Recursing" ~namestr)
(doall (map (fn [child#] (child#)) ~children)))))
Run Code Online (Sandbox Code Playgroud)
这感觉有点不必要,因为根据定义,fn 名称应该仅在其自己的范围内相关。
更新:刚刚使用 alpha 版本进行了测试,似乎 Clojure 1.7.0-alpha3 及更高版本可以在没有此 hack 的情况下工作,Clojure 1.7.0-alpha2 及更早版本已损坏。在 1.7.0 稳定版本发布之前,使用此解决方法可能是可以的,除非有人能想到更好的方法。
| 归档时间: |
|
| 查看次数: |
101 次 |
| 最近记录: |