当transducer与into一起使用时触发异常,但与sequence一起使用时不触发。
(sequence (halt-when #{4}) (range)) ;; => (0 1 2 3)
(into [] (halt-when #{4}) (range))
;; class java.lang.Long cannot be cast to class clojure.lang.ITransientCollection (java.lang.Long is in module java.base of loader 'bootstrap'; clojure.lang.ITransientCollection is in unnamed module of loader 'app')
我认为这是由于一个错误引起的,它返回的是输入而不是结果,我认为这个实现应该修复这个特定问题。
(defn halt-when1
([pred] (halt-when1 pred nil))
([pred retf]
(fn [rf]
(fn
([] (rf))
([result]
(if (and (map? result) (contains? result ::halt))
(::halt result)
(rf result)))
([result input]
(if (pred input)
(reduced {::halt (if retf (retf (rf result) input) (rf result))})
(rf result input)))))))
然而,我认为retf函数现在与变换上下文耦合。因此,如果您想将结果转换成,例如包含引发停止的输入,那么您需要知道在创建transducer时将会在特定上下文中使用,以及您需要调用conj、conj!或类似操作。因此,我认为这个函数的签名更有意义
(defn halt-when2
([pred] (halt-when2 pred nil))
([pred retf]
(fn [rf]
(fn
([] (rf))
([result]
(if (and (map? result) (contains? result ::halt))
(::halt result)
(rf result)))
([result input]
(if (pred input)
(let [r (if retf
(retf rf result input)
result)]
(reduced {::halt (rf r)}))
(rf result input)))))))
这改变了retf函数的签名,以接受减少函数以及结果和输入,现在您可以决定做什么
(into [] (halt-when2 #{4} (fn [rf r i] (rf r i))) (range)) ;; => [0 1 2 3 4]
也许可以向halt-when添加一个第三个参数,一些选项映射,来告诉它我们传递了2个参数的retf还是3个参数的一个?这有意义吗?或者我可能是在要求一个新函数叫stop-at或者是其他什么?
--- 编辑 ---
刚刚发现这篇https://groups.google.com/g/clojure/c/6HvmJIUsXKk/m/gLqUsfcnAwAJ,我认为它更详细地解释了为什么返回输入的原因,但我仍然认为提供一种策略来转换结果,而无需知道实际的减少函数是什么,是有用的。