2012-04-15 38 views
3

假設我有一系列的序列工作的功能,我想用以下方式一起使用:應用幾個集合函數與一個枚舉

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

上面的代碼要枚舉該序列兩次。我對一個函數感興趣,它會給出相同的結果,但只列舉一次序列。該函數將是這樣的:

magicFunction (mean, standardDeviation) data 

其中輸入是函數的元組和一個序列和所述輸出中是與上面的功能是相同的。

這是可能的,如果功能meanstadardDeviation是黑匣子,我不能改變它們的實現?

如果我寫meanstandardDeviation自己,有沒有一種方法,使他們一起工作?也許某種程度上讓他們繼續讓下一個功能的輸入和交付完成後的結果?

回答

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來定義的函數,這意味着它們由一些INI的tial值(第一個例子中的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 
+3

事實上,它是可以計算使用'fold'標準偏差 - 它採用方差的定義 - 見例如http://mathcentral.uregina.ca /QQ/database/QQ.09.02/carlos1.html – 2012-04-16 04:42:39

+0

「我不確定您是否可以通過一次傳遞計算標準偏差」。見Knuth的聖經! – 2012-04-16 18:23:42

+0

@JohnPalmer感謝您的參考! – 2012-04-16 22:26:24

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) 

這確保了序列本身的單次迭代(可能有副作用,或者它的速度慢),但本質上緩存結果這樣做。高速緩存仍然會迭代一次。那有什麼不對嗎?你想達到什麼目的?