使用一个枚举应用多个聚合函数

时间:2012-04-15 22:15:48

标签: f# sequence

假设我有一系列函数用于序列,我想以下列方式一起使用它们:

let meanAndStandardDeviation data = 
    let m = mean data
    let sd = standardDeviation data
    (m, sd)

上面的代码将枚举序列两次。我感兴趣的是一个函数,它会给出相同的结果,但只列举一次序列。这个函数将是这样的:

magicFunction (mean, standardDeviation) data

其中输入是函数和序列的元组,输出与上面的函数相同。

如果函数meanstadardDeviation是黑框并且我无法更改其实现,这是否可行?

如果我自己写了meanstandardDeviation,有没有办法让它们一起工作?也许某种程度上让他们继续产生下一个函数的输入并在结束时移交结果?

2 个答案:

答案 0 :(得分:3)

当函数是黑盒时,仅使用单次迭代执行此操作的唯一方法是使用Seq.cache函数(一次计算序列并将结果存储在内存中)或将序列转换为其他函数内存中表示。

当一个函数将seq<T>作为参数时,你甚至不能保证它只会对它进行一次评估 - 标准偏差的通常实现将首先计算平均值,然后再次迭代序列计算误差的平方。

我不确定你是否可以通过一次通过来计算标准偏差。但是,如果使用fold表示函数,则可以执行此操作。例如,使用两次传递计算最大值和平均值如下所示:

let maxv = Seq.fold max Int32.MinValue input
let minv = Seq.fold min Int32.MaxValue input

你可以使用这样的单一传递:

Seq.fold (fun (s1, s2) v -> 
  (max s1 v, min s2 v)) (Int32.MinValue, Int32.MaxValue) input

lambda函数有点难看,但你可以定义一个组合子来组成两个函数:

let par f g (i, j) v = (f i v, g j v)
Seq.fold (par max min) (Int32.MinValue, Int32.MaxValue) input

这种方法适用于可以使用fold定义的函数,这意味着它们包含一些初始值(第一个示例中为Int32.MinValue),然后是一些用于更新初始值的函数(上一个)当它获得下一个值时的状态(然后可能对结果进行一些后处理)。一般来说,应该可以用这种方式重写单通函数,但我不确定这是否可以用于标准偏差。它绝对可以用于平均值:

let (count, sum) = Seq.fold (fun (count, sum) v -> 
  (count + 1.0, sum + v)) (0.0, 0.0) input
let mean = sum / count

答案 1 :(得分:2)

我们在这里讨论的是具有以下签名的函数:

(seq<'a> -> 'b) * (seq<'a> -> 'c) -> seq<'a> -> ('b * 'c)

如果这是函数的签名,我可以想到没有直接的方法可以使用序列的单次迭​​代来实现上述目的。好吧,没有比这更有效的方法:

let magicFunc (f1:seq<'a>->'b, f2:seq<'a>->'c) (s:seq<'a>) = 
    let cached = s |> Seq.cache
    (f1 cached, f2 cached)

确保序列本身的单次迭代(可能存在副作用,或者它很慢),但通过基本缓存结果来实现。缓存仍然是另一次迭代。那有什么不对吗?你想要实现什么目标?