我之前见过这个错误报告:https://clojure.atlassian.net/browse/CLJ-1569。
现在,在尝试用另一种语言实现 Clojure 类型的变换器以进行教学目的时,我意识到这件事件影响了很多东西。所以先谈一下...
使用当前实现initializing lazily通常需要在步骤函数和完成函数中执行额外的检查,以及空输入的情况。所以并不是没有解决方案,但这非常不直观,特别是所有标准变换器仍然有nulary版本,它们永远不会被调用(所以从实现的角度讲,它在过程中跳过了一步,但从逻辑的角度讲,它添加了一个隐式的步骤,即删除一个转换后的初始值,并用与之无关的值替换它)。
在给出更多示例之前,我应该注意的是,在链接的issus中,alt-transduce没有检查初始化过程后初始值是否已经reduced,我认为它应该这样做,因为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))))))
说到受影响的lazy和额外的检查,示例已经在标准库中。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。
感谢您能进一步提供关于为什么做出这种设计选择的信息。