2012-10-03 8 views
1

我想总结一个('a* 'b)阵列如何制作一个模仿Array.sum的通用Array.sumPair?

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

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

+0

这是为什么不理想?为什么为'tuple'定义'+'和'zero'更好? – Daniel

+0

@丹尼尔假设我正在写个人图书馆,并希望它尽可能高效。我不知道'+'是否更好。 – colinfang

+2

为什么不使用像'Array.fold(fun(a0,b0)(a,b) - > a0 + a,b0 + b)(GenericZero,GenericZero)'这样简单易读的东西? – bytebuster

回答

1

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

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

编辑:或者,如果你想与Array.sum(丹尼尔建议)最大的兼容性,只需添加一个检查空数组:

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),但如果你有一个巨大的数组,它的确具有可并行化的好处。

+0

是否有任何具体原因使用'^ T'而不是''a'? “减少”比“折叠”更好吗? – colinfang

+0

这不与'Array.sum'相同。它不处理空数组。 – Daniel

+0

@Daniel好的,我添加了一个编辑来检查一个空数组。 –

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 
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) 

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

+0

为什么你要为'GenericZero'定义一个函数? 1.我可以使用值绑定,例如'let zero = GenericZero'? 2.我可以直接使用'GenericZero'而不引入'zero()' – colinfang

+2

使用一个值而不是一个函数将会限制这些对是相同的类型,因为它被用作两者的种子值。因为它是一个通用函数,对于任何支持的类型都返回零。 – Daniel