2024 年 Clojure 状态调查! 分享您的想法。

欢迎!请查看 关于 页面以获取更多关于如何使用本站的信息。

+1 投票
序列

我在 clojure.core 的几个地方看到了以下模式

(if (next more)
  (recur ... (next more))
  ...)

其中 next 调用两次,在 recur 之前。

这是否是“故意为之”还是未注意到的代码?

我期望有一个更高效的实现,比如:

(if-let [next' (next more)]
  (recur ... next')
  ...)

3 个答案

+1 投票

这可能是为了避免“抓住序列的头部”的风险。通常,你希望在递归调用之前,序列已被消费的部分可由垃圾收集器回收。

“持有头部”问题是否适用于使用 `recur` 的“尾递归”调用?
+1 投票
by
编辑了 by

这可能是核心中函数定义的顺序问题吗?

浏览core.clj文件时,if-let 宏实际上定义在第1841行

https://github.com/clojure/clojure/blob/653b8465845a78ef7543e0a250078eea2d56b659/src/clj/clojure/core.clj#L1841

而“next more recur next more”片段(在数学内容中)在letif-let存在之前就已经使用(第1055行)

https://github.com/clojure/clojure/blob/653b8465845a78ef7543e0a250078eea2d56b659/src/clj/clojure/core.clj#L1055

defmacro if-let之后的代码实际上使用了您预期的模式(第3690行)

https://github.com/clojure/clojure/blob/653b8465845a78ef7543e0a250078eea2d56b659/src/clj/clojure/core.clj#L3690

我认为这应该像是常规的单遍编译,与常规的Clojure代码类似,函数必须在使用之前定义,无论是宏还是defns。

我不确定为什么顺序要这样,可能和引导(bootstrapping)有关。

by
但`let`是一个特殊形式,标记为`:special-form true`,所以可能最终是因为性能/垃圾回收的原因,正如其他回答所指出的。
0票数
by

我建议使用Criterium库尝试一些性能基准测试,以比较您要修改的函数当前和修改后的版本,并查看它是否会导致代码运行更快:https://github.com/hugoduncan/criterium

我还没有自己进行过这样的测量,但HotSpot JIT编译器有时能做一些相当惊人的事情。

我同意。性能很棘手。

你可能也会对这个有些相似的话题感兴趣
https://ask.clojure.org/index.php/8361/changing-some?show=8361#q8361
我当然是在提问前测试过的。

  (= 1 1 1 1 1 1)

在我的电脑上快了15%。
单个 `(next more)` 大约 10 ns。

但无论如何,这个问题并不是完全关于性能。
它关于在算法中重复已经完成的计算的无用性。
...