如何制作模仿Array.sum的通用Array.sumPair?

时间:2012-10-03 19:29:11

标签: f# inline

我想总结一个('a* 'b)数组

let inline sumPair (array: ('a * 'b)[]) =
    let array1, array2 = array |> Array.unzip
    (Array.sum array1, array2 |> Array.sum)

显然这不太理想。我想也许一种方法是定义+的{​​{1}}和zero,并使用内置的tuple,但找不到任何相关的教程。有帮助吗?

3 个答案:

答案 0 :(得分:4)

let inline sumPair source = 
  let inline zero() = LanguagePrimitives.GenericZero
  Seq.fold (fun (xAcc, yAcc) (x, y) -> (xAcc + x, yAcc + y)) (zero(), zero()) source

正如你在评论中所述,它应尽可能高效,我认为没有什么可以胜过命令式版本:

let inline sumPair source = 
  let mutable xAcc = LanguagePrimitives.GenericZero
  let mutable yAcc = LanguagePrimitives.GenericZero
  for x, y in source do
    xAcc <- xAcc + x
    yAcc <- yAcc + y
  (xAcc, yAcc)

它应该比第一个需要更少的分配。

答案 1 :(得分:1)

如果您担心创建临时数组,这里有一个使用序列的简明版本:

let inline sumPair (array: _ []) =
    Seq.sumBy fst array, Seq.sumBy snd array

稍长的变体,但可能更有效的是:

let inline sumPair (array: _ []) =
        array |> Seq.map fst |> Seq.sum, array |> Seq.map snd |> Seq.sum

答案 2 :(得分:1)

这是另一种方式。这不要求您使用GenericZero:

let inline sumPair (array : (^T * ^U)[]) =
    array
    |> Array.reduce (fun (x0, y0) (x1, y1) ->
        (x0 + x1), (y0 + y1))

编辑:或者,如果您希望与Array.sum最大程度兼容(如Daniel建议的那样),只需添加一个空数组的检查:

let inline sumPair (array : (^T * ^U)[]) =
    if Array.isEmpty array then
        LanguagePrimitives.GenericZero, LanguagePrimitives.GenericZero
    else
        array
        |> Array.reduce (fun (x0, y0) (x1, y1) ->
            (x0 + x1), (y0 + y1))

此代码不会完全内联(IL仍然会调用Array.reduce),但如果你有一个巨大的数组,它确实具有可并行化的好处。