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

而“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

我认为这应该与 core.clj 作为常规 Clojure 代码的常规单次编译相同。必须先定义函数,然后才能使用,无论是宏还是 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)

在我的电脑上快了15%。
单一的 `(next more)` 约10纳秒。

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