F# 如何对3元组序列求和?然后计算他们的平均值?

F# 如何对3元组序列求和?然后计算他们的平均值?,f#,f#-interactive,F#,F# Interactive,我有一个3元组序列,如下所示: [(123, 143 ,136), (177, 284, 248), (143, 182, 252)...] 我想通过对所有3元组求和,将它们转换成单个3元组: [((x1+x2+x3),(y1+y2+y3),(z1+z2+z3))] > let sumRGB (r1,g1,b1) (r2,g2,b2) = (r1+r2, g1+g2, b1+b2);; val sumRGB : r1:int * g1:int * b1:int -> r2:

我有一个3元组序列,如下所示:

[(123, 143 ,136), (177, 284, 248), (143, 182, 252)...]
我想通过对所有3元组求和,将它们转换成单个3元组:

[((x1+x2+x3),(y1+y2+y3),(z1+z2+z3))]
> 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
> let sumRGBSeq seq = seq |> Seq.fold sumRGB (0,0,0);;

val sumRGBSeq : seq:seq<int * int * int> -> int * int * int
然后找到他们的平均值

[((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)
我尝试使用:


在您的示例中,不需要最后一个表达式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获得序列的长度,但这将再次迭代序列。因此,最好将计数作为另一个参数保持在折叠状态:

请注意,这将使用整数进行计算-因此它也将进行整数除法。如果您使用的是浮点数,只需将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 }
该记录具有重载的+运算符、零成员和DivideByInt方法,这使得以下操作成为可能:

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

让我们一步一个脚印地从F Interactive列表中了解一下类型推断是如何处理代码的

两个3元组之和:

[((x1+x2+x3),(y1+y2+y3),(z1+z2+z3))]
> 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
> let sumRGBSeq seq = seq |> Seq.fold sumRGB (0,0,0);;

val sumRGBSeq : seq:seq<int * int * int> -> int * int * int
三元组的和序列:

[((x1+x2+x3),(y1+y2+y3),(z1+z2+z3))]
> 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
> let sumRGBSeq seq = seq |> Seq.fold sumRGB (0,0,0);;

val sumRGBSeq : seq:seq<int * int * int> -> int * int * int
更新


正如@TomasPetricek所指出的,您当前的代码不计算平均值,而是计算3元组序列的总和

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

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

哦,我忽略了一个事实,你必须除以元素的数量才能得到运算代码中已经缺少的部分的平均值。哈,我明白了,大写/小写不起作用。当我测试这行代码时,它显示错误FS0001:“int*int*int”类型与“Seq”类型不兼容。我非常喜欢您的解决方案,即向记录中添加静态成员以使该类型能够与Seq.average一起工作,我只想补充一点,在没有Tomas Petricek帮助的情况下,发现这种情况的一种方法是检查类型签名:Seq.average:Seq->^t需要^t加上静态成员+和^t加上静态成员DivideByInt和^t加上静态成员零使用一个单独的sumRGB函数有一定的优点,因为你可以很好地写出seq |>seq.fold sumRGBP 0,0,0:-是的,这比使用lambda要好得多!谢谢