F#和#x27+';运算符重载和List.fold
我试图在定义F#和#x27+';运算符重载和List.fold,f#,overloading,record,operator-keyword,fold,F#,Overloading,Record,Operator Keyword,Fold,我试图在定义+运算符重载的记录类型上使用List.fold,但在尝试将(+)运算符用作传递给fold的lambda时,遇到类型不匹配错误。下面是一个简单的片段,它举例说明了我的问题: // a record type that also includes an overload for '+' type Person = { Name : string; Age: int } static member ( + ) (x: Person, y: Person) = x.Age
+
运算符重载的记录类型上使用List.fold,但在尝试将(+)
运算符用作传递给fold的lambda时,遇到类型不匹配错误。下面是一个简单的片段,它举例说明了我的问题:
// a record type that also includes an overload for '+'
type Person =
{ Name : string; Age: int }
static member ( + ) (x: Person, y: Person) = x.Age + y.Age
+
重载工作正常
> jen + kevin;;
val it : int = 87
但假设我有一张名单:
> let people = [kevin;jen];;
我无法使用List.fold对所有年龄进行求和:
> List.fold (+) 0 people;;
List.fold (+) 0 people;;
----------------^^^^^^
error FS0001: Type constraint mismatch. The type
int
is not compatible with type
Person
The type 'int' is not compatible with the type 'Person'
我猜问题在于,当以这种方式传递时,F#无法识别+
的重载,因为fold隐式地将列表键入int
,因为我使用了“0”作为累加器。我不确定是否有可能让我的自定义操作符重载正常工作,如果有可能,我缺少什么来实现它。(我假设这是可能的,因为您可以在浮动上使用+
)
编辑
我知道问题在于类型不匹配。正如JaredPar所写的,我可以写一个lambda来记录两个人的记录并加上年龄。这不是我的观点。问题是,在我看来,应该有一种方法让我已经编写的+
操作符重载被fold确认为有效重载
另一次编辑
谢谢大家的意见。有一件事变得越来越清楚,那就是我不可能做我想做的事,但那很好。我学到了一些东西!我所看到的是,运算符重载的解决方案是这样的,即它们不能在所有上下文中都起作用——因此,对于
fold
,没有无缝的方法使+
作为lambda传递,就像用作中缀alajen+kevin
一样。这完全可以解释为什么这不起作用。人们建议解决这个问题的解决方案基本上是一次性的,用于处理特定的折叠问题
——我真正想要的是如何让正确的操作员重载在每个情况下都被挑选出来(即折叠
,等等)--我不想为了处理列表而编写一堆特殊的案例代码。很明显,F#的操作符重载解决方案有一些限制,使其工作到一个肤浅的层次,这很好。列表。fold函数采用类型为State->T->State
的lambda/函数。本例中的+
运算符的类型为Person->Person->int
,与签名不兼容。这就是为什么会出现错误
要折叠年龄,请尝试以下方法
people |> List.fold (fun sum p -> sum + p.Age) 0
这里使用+
操作符作为折叠的一部分的一种方法是将Person
映射到Age属性,然后对int+
操作符使用折叠
people
|> Seq.ofList
|> Seq.map (fun p -> p.Age)
|> Seq.fold (+) 0
这个(+)过载怎么样
type Person =
{ Name : string; Age: int }
static member ( + ) (x: Person, y: Person) = { Name = x.Name + " and " + y.Name; Age = x.Age + y.Age }
let jen = { Name = "Jen"; Age = 20 }
let kevin = { Name = "Kevin"; Age = 40 }
[jen; kevin] |> List.fold (+) { Name = ""; Age = 0 };;
会回来的
val it : Person = {Name = "Jen and Kevin";
Age = 60;}
有道理吗
<>但是,如果你觉得一组人的查找摘要年龄是你的<代码>人<代码>类的组成部分,你可以考虑使用相应的静态类成员<代码>群组< /代码>,而不是重载<代码>(+)< /代码>:
并在需要时使用,如下所示:
[jen; kevin] |> Person.GroupAge
这里有一个合理的解决方案,可能对您有用
type Person = {
Name : string
Age: int
} with
static member (+) (x: Person, y: Person) =
{ Set = Set.ofList [x; y]; SumOfAges = x.Age + y.Age }
and People = {
Set:Person Set
SumOfAges:int
} with
static member (+) (x:People, y:Person) =
{ x with Set = x.Set.Add y; SumOfAges = x.SumOfAges + y.Age }
static member Empty =
{ Set = Set.empty; SumOfAges = 0 }
let p = [ { Name = "Matt"; Age = 32; }; { Name = "Dan"; Age = 26; } ]
let r = p |> List.fold (+) People.Empty
我认为你的问题是概念性的。传递给List.fold的是单个函数。最好将
+
看作是一整堆不同函数的语法糖,使用类型签名,如int->int->int
、float->float
和person->person->int
那么,当编译器看到以下内容时会发生什么:
List.fold (+) 0 people;;
因此我们有一个person
列表以及一个默认参数0
,它是一个int
。所以我们看一下fold的签名
List.fold : ('State -> 'T -> 'State) -> 'State -> 'T list -> 'State
根据0
,解释这一点的一种方法可能是'State=int
。因此,我们需要找到一个重载+
,如下所示
int -> Person -> int
这当然不存在。然后,您可以使用它为+
操作符提供更好的定义。差不多
// a record type that also includes an overload for '+'
type Person =
{ Name : string; Age: int }
static member ( + ) (x: int, y: Person) = x + y.Age
问题是,than+是为加法整数、浮点等定义的,而您为add 2 person定义了一个+。但是当您尝试时:
List.fold (+) 0 people;;
您正在尝试与一个人(多人)添加int(0)
那就是
事实上,当你添加两个人时,这将返回一个整数…然后每次迭代人时,你将得到累积(整数)和人(人列表)
现在..在不添加更多重载或泛型的情况下解决此问题的简单方法是:
[kevin;jen] |> List.fold (fun acc person -> acc + person.Age) 0
类似于中的示例
…谢谢。您的解决方案存在的问题是,您没有使用已在“+”运算符重载中编写的功能。我理解类型不匹配——我不想做的是重写lambda来完成我已经用操作符重载编写的操作。我的问题是:可能吗?如果可能,怎么可能?@KevinWon只要您编写的运算符与fold函数签名不兼容,就不可能。使其工作的唯一方法是,如果您的
+
获取2个Person
对象并返回Person
您的+
成员没有意义。为什么把两个人加在一起会得到他们年龄的总和?下面给了你很多很好的解释。我建议您自己尝试编写fold函数,看看为什么您不应该期望它以您认为的方式运行。@ChaosPandion:这个示例是一个愚蠢的设置,用来显示原理——这不是我实际想做的——我做了一个愚蠢的示例来清楚地显示问题。@Kevin Won-您能展示一下您认为这样一个问题的代码吗操作有意义吗?@Kevin-我不同意你在“另一次编辑”部分的结论。您可以像内置操作符一样使用(+)
操作符,只要它有意义,F#就会识别并使用它,而不会出现任何问题(例如Li
[kevin;jen] |> List.fold (fun acc person -> acc + person.Age) 0
let data = [("Cats",4);
("Dogs",5);
("Mice",3);
("Elephants",2)]
let count = List.fold (fun acc (nm,x) -> acc+x) 0 data
printfn "Total number of animals: %d" count