我对F#中的默认“ = ”(等于)运算符有疑问。它允许比较用户定义的联合类型。问题是:它的复杂性是什么?例如,让我们考虑以下类型:
type Tree<'a> =
| Nil
| Leaf of 'a
| Node of Tree<'a> * Tree<'a>
以及树木:
let a : Tree<int> = Node (Node (Node (Leaf 1, Leaf 2), Node (Leaf 3, Node (Leaf 4, Leaf 5))), Node (Leaf 6, Nil))
let b : Tree<int> = Node (Node (Node (Leaf 1, Leaf 2), Node (Leaf 3, Node (Leaf 4, Leaf 5))), Node (Leaf 6, Nil))
let c : Tree<int> = Node (Node (Node (Leaf 1, Leaf 2), Nil), Node (Node (Leaf 3, Node (Leaf 4, Leaf 5)), Leaf 6))
很明显这段代码:
printfn "a = b: %b" (a = b)
printfn "a = c: %b" (a = c)
printfn "a = a: %b" (a = a)
生成此输出:
a = b: true
a = c: false
a = a: true
我希望“ a = b ”和“ a = c ”比较需要线性时间。但是“ a = a ”呢?如果它是不变的更复杂的结构,如那个:
let d : Tree<int> = Node (a, c)
let e : Tree<int> = Node (a, c)
是否会通过整个 d 和 e 结构,还是会停在“ a = a ”和“ c = ç的“?
答案 0 :(得分:4)
F#使用结构相等,而.NET中的默认Equals
实现使用引用相等性。这意味着,在典型情况下,相等比较是 O(N),其中 N 是被比较的对象图中的字段数。
如果要确保优化a = a
,可以覆盖Equals
以首先检查引用相等性,否则返回结构相等性。您需要使用[<CustomEquality>]
注释您的类型。
您可以在the F# source code on github中看到相当冗长的结构平等实现。要跟随呼叫层次结构,请从GenericEqualityObj
on line 1412开始。
答案 1 :(得分:-1)
编辑:原来的回答是错误的。
.Net中Equals()
的通常实现方式如下:
true
。false
。false
,否则返回true
。出于某种原因,F#跳过第一步,这意味着时间复杂度始终是线性的。
由于编译器知道a
和b
是相同的,并且c
的某些子树与a
的某些子树相同,并且它也知道它们是不可变的,它理论上可以使a
和b
成为同一个对象,并在c
中重用它们的一些部分。运行时与字符串类似,称为string interning。但是(基于反编译代码)似乎编译器目前没有这样做。