我之前见过这个错误报告:https://clojure.atlassian.net/browse/CLJ-1569。
现在,在尝试为教学目的在另一种语言中实现类似Clojure的转换器时,我意识到这影响了多少东西。所以先是一些想法...
通常情况下,使用当前实现以懒方式执行初始化需要在步函数和空输入的情况下的完成函数中执行额外的检查。所以并非没有解决方案,但这非常不可直观,特别是对于所有标准转换器仍然有0参数版本,这些版本永远不会被调用(在实现上,它是跳过过程中的一个步骤,但在逻辑上,它是添加一个删除转换初始值并用无关值替换它的隐式步骤)。
在给出任何示例之前,我应该指出,链接的问题中的alt-transduce没有检查在初始化过程之后初始值是否已经被归约,我认为它应该这样做,因为减少自身并不这样做。所以我的更新的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。
我会很感激您能提供的有关为什么做出这种设计选择的任何进一步见解。