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

欢迎!请查阅 关于 页面以了解有关此功能的一些更多信息。

+1 子弹
序列

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

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

其中在调用 `recur` 之前调用了两次 next

这是“按设计”的,还是仅仅是未被注意到的代码?

我期望更高效一点的实现,例如

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

3 个答案

+1 子弹

这可能是为了避免“握住序列的头部”的风险。一般来说,在递归调用之前,想要使已经消费掉的序列部分可回收。

“握住头部”问题适用于使用 `recur` 的“尾递归”调用吗?
+1 子弹

这是否是核心中函数定义的顺序导致的?

快速浏览core.clj,发现if-let宏实际上定义在第1841行

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

而在使用“下一更再下一更”片段(数学相关)之前,并未定义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代码一样,使用core.clj进行单遍编译。你需要先定义函数,然后才能使用它们,无论是宏还是defns。

我不确定为什么顺序必须是这样的,也许与启动过程有关。

但是`let`是一个特殊形式,具有`:special-form true`属性,所以也许它真的是为了性能/垃圾收集原因,正如其他答案所指出的。
0投票

我建议您尝试使用当前版本和修改后的函数进行一些性能基准测试,并使用Criterium库对Clojure/Java进行性能测量: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)

在我的PC上快15%。
单一的`(next more)`大约是10纳秒。

但不管怎样,这个问题并不完全是关于性能的。
它关于算法中已经完成的计算的无效重复。
...