3

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

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

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

magicFunction (mean, standardDeviation) data

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

如果函数meanstadardDeviation是黑匣子并且我无法更改它们的实现,这可能吗?

如果我meanstandardDeviation我自己一起写,有没有办法让他们一起工作?也许以某种方式让他们继续为下一个函数提供输入并在完成后交出结果?

4

2 回答 2

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
于 2012-04-16T00:11:10.690 回答
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)

这确保了序列本身的单次迭代(可能有副作用,或者它很慢),但实际上是通过缓存结果来实现的。缓存仍会再次迭代。这有什么问题吗?你想达到什么目的?

于 2012-04-15T23:55:06.893 回答