如何将3元组的序列相加?并计算他们的平均值?

时间:2014-04-07 22:29:22

标签: f# f#-interactive

我有一系列像这样的3元组:

[(123, 143 ,136), (177, 284, 248), (143, 182, 252)...]

我想通过总结所有3元组来将它们变成单个3元组:

[((x1+x2+x3),(y1+y2+y3),(z1+z2+z3))]

然后找到他们的平均值

[((x1+x2+x3)/n,(y1+y2+y3)/n,(z1+z2+z3)/n)]

我尝试总结它们:

let averageRGB seqRGB = seqRGB |> Seq.fold(fun(R, G, B)(r, g, b) -> (R+r, G+g, B+b)) (0, 0, 0) (r, g, b)

我尝试使用:

How do you sum up and average a Sequence?

Sum of the first elem of a 3-tuples list

http://social.msdn.microsoft.com/Forums/vstudio/en-US/776d6f05-40c9-43ac-9b1c-eab188466eed/f-how-to-use-foldback-for-array-of-tuples?forum=fsharpgeneral

3 个答案:

答案 0 :(得分:8)

在您的示例中,不需要最后一个表达式(r, g, b)。没有它,您的代码会正确地对值进行求和。使用变量的大写名称并不是特别惯用,因此我将其写为:

let averageRGB seqRGB = 
  seqRGB |> Seq.fold (fun (r1,g1,b1) (r2,g2,b2) -> (r1+r2, g1+g2, b1+b2)) (0, 0, 0)

要计算平均值,您只需要将结果除以元素数。您可以使用Seq.length来获取序列的长度,但这会再次迭代序列。因此,最好将计数保持为fold状态的另一个参数:

let averageRGB seqRGB = 
  let r,g,b,count = seqRGB |> Seq.fold (fun (r1,g1,b1,count) (r2,g2,b2) ->
        (r1+r2, g1+g2, b1+b2, count+1)) (0, 0, 0, 0)
  (r/count, g/count, b/count)

请注意,这使用整数进行计算 - 因此它也会进行整数除法。如果您正在使用浮点数,则可以将0常量更改为0.0,F#将推断该函数适用于浮点数。

如果您想要更高级的解决方案,那么您可以定义自己的类型来表示颜色。对于正确的类型定义,您只需使用Seq.average即可获得平均值。所以,如果你想使用记录类型,你可以写:

type Color =
 { R : int; G : int; B : int }
 static member (+) (c1, c2) =
  { R = c1.R + c2.R; G = c1.G + c2.G; B = c1.B + c2.B }
 static member DivideByInt(c, n) =
  { R = c.R / n; G = c.G / n; B = c.B / n} 
 static member Zero = { R=0; G=0; B=0 }

该记录包含一个重载+运算符,Zero成员和DivideByInt方法,可实现以下目标:

[ {R=255;G=0;B=0}; {R=0;G=128;B=255} ]
|> Seq.average

答案 1 :(得分:1)

让我们一步一步(来自F#Interactive的列表,以显示类型推断如何处理我们的代码)。

  1. 总结两个3元组:

    > let sumRGB (r1,g1,b1) (r2,g2,b2) = (r1+r2, g1+g2, b1+b2);;
    
    val sumRGB :
      r1:int * g1:int * b1:int -> r2:int * g2:int * b2:int -> int * int * int
    
  2. 3元组的总和序列:

    > let sumRGBSeq seq = seq |> Seq.fold sumRGB (0,0,0);;
    
    val sumRGBSeq : seq:seq<int * int * int> -> int * int * int
    
  3. 测试解决方案:

    > let source = [(123,143,136);(177,284,248);(143,182,252)];;
    
    val source : (int * int * int) list =
      [(123, 143, 136); (177, 284, 248); (143, 182, 252)]
    
    > sumRGBSeq source;;
    
    val it : int * int * int = (443, 609, 636)
    
  4. <强>更新

    当@TomasPetricek指针输出时,您当前的代码不计算平均值,它计算3元组序列的总和。

答案 2 :(得分:0)

这个版本不包括平均值(正如Tomas指出的那样,内置平均值不适用于int),使用List代替Seq,它可能效率不高,但它更简洁:

let (|||>) (a,b,c) fn  = (fn a, fn b, fn c)
let sumRGB rgb =  rgb |> List.unzip3 |||> List.sum