gpt4 book ai didi

.net - F# 函数耦合迭代 : performance issue and preferred functional style

转载 作者:行者123 更新时间:2023-12-04 17:44:01 25 4
gpt4 key购买 nike

我正在阅读 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 列表 -> 浮点数),这是匹配子句上的一个模式,但我认为它太过分了)。

然后 blurArray 的等价物变为:
let blurArray' (arr:float[]) = 
let mappingFcn = arr |> computeElementsAverage
arr |> (Array.mapi mappingFcn)

最后我的问题:

首先,最推荐的功能性处理方式是什么?我特别不喜欢由于 Array.mapi 的要求而被迫在 computeElementsAverage 中声明元素类型 (float) 的最终元素这一事实。有没有更好的方法来做到这一点,避免一个不会被使用的参数?

其次:我的代码在性能上要慢得多,这是意料之中的;但它的运行速度比原始代码快 1/10;任何其他解决方案仍然依赖高阶函数而不会对性能造成如此大的影响?

最后,在依赖于多个索引的数据结构(例如:数组)中执行计算的一般首选方法是什么?如您所见,我提出的方法是使用 mapi 扫描功能并使用包含结构本身的闭包;你的首选方法是什么?

P.S.:(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左右邻居(由于 maxmin 检查,这不会越界)。现在您可以将模糊写为:
arr |> fillArray 1 Seq.average

fillArray ,创建子数组会有点低效 - 您可能可以通过使用 ArraySegment 使其更快或者通过将数组的相关部分复制到本地可变数组。但它确实看起来很漂亮和实用!

关于.net - F# 函数耦合迭代 : performance issue and preferred functional style,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42877295/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com