【问题标题】:Aggregating transducers with intermediate values聚合具有中间值的传感器
【发布时间】:2020-11-17 18:38:27
【问题描述】:

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

比如下面的表达式

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

产生(#{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:另一种表述方式是,我想要一个执行归约功能并在每一步返回累加器的转换器,它还可以重用所有可用的转换器,因此我不需要重写基本功能。

【问题讨论】:

    标签: clojure transducer


    【解决方案1】:

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

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

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

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

    这里构建结果的函数是conj,我们将其称为步进函数transducer 是一个函数,它将一个阶跃函数作为输入并返回一个新的阶跃函数作为输出。所以如果我们想把reductions和一个transducer结合起来,我们可以把transducer应用到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 库中还有一个 transducer 版本的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)
    

    【讨论】:

    • 您好,感谢您的回答。我想澄清一下,在这种情况下使用传感器很重要,因为我正在使用它们从来自通道的一系列输入值中提取指标,然后将其输入另一个通道。问题是我还想获得这些指标的中间值。所以,现在想象一下,我想对它们应用一些操作,而不是仅仅将元素添加到集合中。比如,增加和过滤奇数。我该怎么做?
    • 查看最后一个代码 sn-p 以了解如何将xforms/reductions 与过滤换能器和采集换能器结合使用。
    猜你喜欢
    • 2013-04-28
    • 2018-05-09
    • 2013-05-09
    • 1970-01-01
    • 1970-01-01
    • 2019-06-01
    • 2020-11-03
    • 2015-10-07
    • 2015-12-13
    相关资源
    最近更新 更多