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

欢迎!请参阅关于页面,了解这份工作的一些更多信息。

+1
序列

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

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

其中调用了两次 `next` 然后 `recur`。

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

我希望有更高效的实现,如下

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

3 条答案

+1

可能是为了避免持有序列的“头部”的风险。一般来说,希望在递归调用之前,序列的已经消费部分可以被垃圾回收。

“持有头部”问题适用于使用`recur`进行的“尾递归”调用吗?
+1

编辑

这是否是Core中函数定义的顺序?

在查看core.clj时,发现if-let宏实际上是在第1841行定义的

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

但是“下一个更多recur下一个更多”(在数学素材中)是在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"`所以可能最终是为了性能/gc的原因,正如其他答案所指出的。
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 ns。

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