0

我仍在尝试更好地了解如何在 clojure 中使用传感器。在这里,我有兴趣应用聚合传感器,例如https://github.com/cgrand/xforms中的传感器,但在每一步都报告计算的中间值。

例如,下面的表达式

(sequence (x/into #{}) [1 2 3])

yield (#{1 2 3}),这只是减少的最终值。xf-incremental现在,我会对给出类似的换能器感兴趣

(sequence (comp xf-incremental (x/into #{})) [1 2 3])

产量(#{1} #{1 2} #{1 2 3})

我对此感兴趣的原因是我想报告一个指标的中间值,该指标聚合了已处理值的历史记录。

知道如何以通用方式进行此类操作吗?

编辑:将 (x/into #{}) 视为聚合结果的任意转换器。更好的例子可能是我期望的 x/avg 或 (x/reduce +)

(sequence (comp xf-incremental x/avg) [1 2 3])
(sequence (comp xf-incremental (x/reduce +)) [1 2 3])

返回(1 3/2 2)(1 3 6)分别。

编辑2:另一种表述方式是,我想要一个执行缩减功能并在每一步返回累加器的转换器,它还可以重用所有可用的转换器,因此我不需要重写基本功能。

4

1 回答 1

3

使用 clojure.core/reductions 的解决方案

您不需要换能器来执行您要求的计算。调用您要查看的所有中间结果的reduce函数reductions,并为它提供conj一个空集作为参数:

(rest (reductions conj #{} [1 2 3]))
;; => (#{1} #{1 2} #{1 3 2})

rest删除第一个空集,因为那是您在原始问题中请求的输出。

在这里建立结果的函数是conj,我们将其称为阶跃函数换能器是一个函数,它将一个阶跃函数作为输入并返回一个新的阶跃函数作为输出。因此,如果我们想reductions与传感器结合,我们可以将传感器应用于conj

(def my-transducer (comp (filter odd?)
                         (take 4)))

(dedupe (reductions (my-transducer conj) #{} (range)))
;; => (#{} #{1} #{1 3} #{1 3 5} #{7 1 3 5})

dedupe是否只是删除与前面元素相等的元素。如果您不想这样做,可以将其删除。在这种情况下,您会得到以下信息,因为这就是过滤转换器的工作方式:

(reductions (my-transducer conj) #{} (range)))
;; => (#{} #{} #{1} #{1} #{1 3} #{1 3} #{1 3 5} #{1 3 5} #{7 1 3 5})

使用 net.cgrand.xforms/reductions 的基于换能器的解决方案

显然,xforms 库中还有一个转换器版本reductions,它更接近您的初始代码:

(require '[net.cgrand.xforms :as xforms])

(rest (sequence (xforms/reductions conj #{}) [1 2 3]))
;; => (#{1} #{1 2} #{1 3 2})

xforms/reductions转换器可以与其他转换器组合使用comp,例如过滤奇数并获取其中的前四个:

(sequence (comp (filter odd?)
                (take 4)
                (xforms/reductions conj #{}))

          (range))
;; => (#{} #{1} #{1 3} #{1 3 5} #{7 1 3 5})

在这种情况下,您不需要dedupe. 也可以使用其他阶跃函数xforms/reductions,例如+

(sequence (comp (filter odd?)
                (take 10)
                (xforms/reductions + 0)
                (filter #(< 7 %)))

          (range))
;; => (9 16 25 36 49 64 81 100)
于 2020-07-28T11:52:03.790 回答