我之前看到过这个错误报告: https://clojure.atlassian.net/browse/CLJ-1569。
现在,在尝试为了教育目的在另一种语言中实现类似Clojure的转换器时,我意识到这一点影响了多少东西。所以我首先分享一下...
在一般情况下,使用当前的实现进行懒惰初始化需要在步骤函数和完成函数中进行额外的检查,以防空输入。所以并不是没有解决方案,但这非常不明智,特别是所有标准转换器都有不会调用的空参数版本,所以在实现过程中,它跳过了过程中的一个步骤,但在逻辑上,它增加了一个显式的步骤,丢弃变换后的初始值并用一个无关的值替换它。
在给出更多的例子之前,我应该指出,在链接的问题中,alt-transduce没有检查初始化过程是否已经对初始值进行了归约,我认为它应该这样做,因为reduce本身不做这一点。所以我的更新后的alt-reduce是
(defn alt-transduce
([xform f coll]
(let [rf (xform f)
result (rf)]
(rf (if (reduced? result)
(unreduced result)
(reduce rf (rf) coll)))))
([xform f init coll]
(let [rf (xform
(fn
([] init)
([result] (f result))
([result input] (f result input))))
result (rf)]
(rf (if (reduced? result)
(unreduced result)
(reduce rf (rf) coll))))))
谈到懒惰和额外的检查所受的影响,标准库中已经有了例子。take被定义为
(defn take
...
([n]
(fn [rf]
(let [nv (volatile! n)]
(fn
([] (rf))
([result] (rf result))
([result input]
(let [n @nv
nn (vswap! nv dec)
result (if (pos? n)
(rf result input)
result)]
(if (not (pos? nn))
(ensure-reduced result)
result)))))))
...
首先,take 0仍然需要消费一个项目,因为它第一次获得控制权。第二,虽然我不知道为什么它以这种相当复杂的方式实现,但只有一个值需要跟踪,但显然有两个比较,显然是因为它必须在其额外的输入中处理n = 0。有工作初始化阶段,可以实现如下
(defn alt-take
([n]
(fn [rf]
(let [nv (volatile! n)]
(fn
([]
(let [result (rf)]
(if (not (pos? n))
(ensure-reduced result)
result)))
([result] (rf result))
([result input]
(let [nn (vswap! nv dec)
result (rf result input)]
(if (not (pos? nn))
(ensure-reduced result)
result))))))))
(类似于原文,必要时可重新排序,要点是两个比较出现在它们自然应该出现的地方.)
如果这个问题还没有修复,那么肯定有一些强烈的理由?这似乎被质疑的答案从未被回答过,我想我的问题在这个点上也是一样的: https://clojure.atlassian.net/browse/CLJ-1569?focusedCommentId=18296。
我很感谢您能提供的关于为什么做出这种设计选择的任何进一步的见解。