繁体   English   中英

F#功能耦合迭代:性能问题和首选功能风格

[英]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邻居(由于maxmin检查,这不会超出界限)。 现在你可以把模糊写成:

arr |> fillArray 1 Seq.average

fillArray ,创建子数组效率会有点低 - 通过使用ArraySegment或将数组的相关部分复制到本地可变数组,可能会使其更快。 但它看起来确实很好用而且功能齐全!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM