[英]F# functional coupled iteration: performance issue and preferred functional style
我正在閱讀Tomas Petricek和Jon Skeet的優秀書籍“真實世界的功能編程”(兩位SO大師,順便說一遍,感謝他們兩位)。 以下函數(第277頁)介紹了一種計算陣列上三點平均值的方法,該方法將邊值視為特殊值:
let blurArray (arr:float[]) =
let res = Array.create arr.Length 0.0
res.[0] <- (arr.[0] + arr.[1]) / 2.0
res.[arr.Length-1] <- (arr.[arr.Length - 2] + arr.[arr.Length -1 ] )/2.0
for i in 1 .. arr.Length - 2 do
res.[i] <- (arr.[i-1] + arr.[i] + arr.[i+1]) / 3.0
res
我理解該函數對於外部世界是不可變的,即使內部采用變異和賦值。 而且,雖然它確實是必要的,但我可以編寫並采用保留聲明式的風格。 不過,我試圖提出一個功能性解決方案,作為練習以更熟悉更高階函數等。 我將報告我的解決方案,然后將表達我的問題。 首先,我定義一個輔助函數
let computeElementsAverage (myArray:float[]) myIndex (myElement:float) =
match myIndex with
| 0 -> (myArray.[0..1] |> Array.average )
| lastInd when lastInd = (myArray.Length -1 ) -> (myArray.[lastInd-1..lastInd] |> Array.average )
| anIndex -> (myArray.[anIndex -1 .. anIndex + 1 ] |> Array.average )
(我可以抽象出(indeces - > float列表)這是匹配子句中的模式,但我認為這是一種矯枉過正的行為)。
然后,blurArray的等效變為:
let blurArray' (arr:float[]) =
let mappingFcn = arr |> computeElementsAverage
arr |> (Array.mapi mappingFcn)
最后我的問題:
首先,最重要的推薦功能是什么? 我特別不喜歡這樣的事實:由於Array.mapi的要求,我被迫在computeElementsAverage中聲明元素類型(float)的最后一個元素。 有沒有更好的方法來做到這一點,避免一個不會被使用的論點?
其次:在性能方面,我的代碼要慢得多,這是預期的; 但它比原始代碼快1/10; 任何其他解決方案仍然依賴於高階函數而不會在性能上受到如此大的打擊?
最后,通過數據結構(例如:數組)執行計算的一般首選方法是什么,這取決於幾個多個indeces? 正如你所看到的,我提出的方法是使用mapi掃描功能並使用包含結構本身的閉包; 你最喜歡的方法是什么?
PS :( blurArray的原始版本使用int []作為輸入,我剛剛修改為float []以在我的版本中使用List.average)
我認為一個更好的,更實用的替代方案是使用Array.init
。 此函數允許您通過指定用於計算每個位置的元素的函數來創建數組。
這仍然看起來非常像原始代碼,但它現在不需要任何顯式變異(現在隱藏在Array.init
)。 事實上,我可能會在本書的修訂版本中使用它:-)。
let blurArray (arr:float[]) =
Array.init arr.Length (fun i ->
if i = 0 then (arr.[0] + arr.[1]) / 2.0
elif i = arr.Length - 1 then (arr.[arr.Length - 2] + arr.[arr.Length - 1] )/2.0
else (arr.[i-1] + arr.[i] + arr.[i+1]) / 3.0 )
接下來,您可以決定在要對陣列中每個點的鄰域執行某些操作時要編寫更多函數 - 您可能需要指定鄰域大小。 你可以這樣寫:
let fillArray offset f (arr:float[]) =
Array.init arr.Length (fun i ->
f arr.[max 0 (i-offset) .. min (arr.Length-1) (i+offset)])
這里,函數f
用每個點的鄰居的子數組調用,左邊和右邊最多offset
鄰居(由於max
和min
檢查,這不會超出界限)。 現在你可以把模糊寫成:
arr |> fillArray 1 Seq.average
在fillArray
,創建子數組效率會有點低 - 通過使用ArraySegment
或將數組的相關部分復制到本地可變數組,可能會使其更快。 但它看起來確實很好用而且功能齊全!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.