我正在尝试在定义+
的运算符重载的记录类型上使用List.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
没有无缝方式使{{1}作为一个lambda工作传递就像用作中缀ala +
时那样。这完全有道理,为什么这不起作用。人们建议解决这个问题的解决方案基本上是一次性处理jen + kevin
的特定问题 - 我真正想到的是如何获得正确的运算符重载以获取每个< / strong>情况(即fold
等) - 我不想写一堆特殊案例代码来处理列表。很明显不是F#的运算符重载分辨率有一些限制,使其适用于皮肤深层次,这很好。
答案 0 :(得分:7)
List.fold
函数采用State -> T -> State
类型的lambda /函数。在这种情况下,+
运算符的类型Person -> Person -> int
与签名不兼容。这就是你收到错误的原因。
要折叠年龄,请尝试以下
people |> List.fold (fun sum p -> sum + p.Age) 0
在此处使用+
运算符作为折叠的一部分的一种方法是将Person
映射到Age属性,然后对int +
运算符使用fold。
people
|> Seq.ofList
|> Seq.map (fun p -> p.Age)
|> Seq.fold (+) 0
答案 1 :(得分:4)
这是一个可能对您有用的合理解决方案。
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
答案 2 :(得分:3)
我认为你的问题是概念性的。传递给List.fold
的是一个单一的功能。最好将+
视为一堆不同函数的语法糖 - 使用int -> int -> int
,float -> float -> float
和person -> person -> int
等类型签名。
那么当编译器看到这种情况时会发生什么:?
List.fold (+) 0 people;;
因此,我们有一个person
列表以及一个0
的默认参数int
。所以我们看一下fold
List.fold : ('State -> 'T -> 'State) -> 'State -> 'T list -> 'State
根据'State = int
,解释此问题的一种方法可能是0
。因此,我们需要找到+
的重载,看起来像
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
答案 3 :(得分:2)
这个(+)超载怎么样?
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;}
有道理吗?
但严重的是,如果您认为找到一组人的摘要年龄是您Person
课程不可或缺的一部分,您可以考虑将相应的静态班级成员GroupAge
设为(+)
而不是重载type Person =
{ Name : string; Age: int }
static member GroupAge = List.fold (fun age person -> age + person.Age) 0
:
[jen; kevin] |> Person.GroupAge
并在需要时使用它,如下所示:
{{1}}
答案 4 :(得分:1)
问题是+是为添加整数,浮点数等定义的,你定义一个+为2加人..但是当你尝试:
List.fold (+) 0 people;;
你正在尝试添加一个人(人)的int(0) 那是!..
实际上当你添加2个人时,这会返回一个整数...然后每次比你迭代的人都会得到累积(整数)和人(人名单)......
现在......解决这个问题的简单方法是在不添加更多重载或泛型的情况下尝试:
[kevin;jen] |> List.fold (fun acc person -> acc + person.Age) 0
类似于http://msdn.microsoft.com/en-us/library/dd233224.aspx
中的示例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
...