F#等于运营商的复杂性

时间:2012-01-16 18:34:07

标签: f# complexity-theory equals operator-keyword user-defined-types

我对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 = ç的“?

2 个答案:

答案 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#跳过第一步,这意味着时间复杂度始终是线性的。

由于编译器知道ab是相同的,并且c的某些子树与a的某些子树相同,并且它也知道它们是不可变的,它理论上可以使ab成为同一个对象,并在c中重用它们的一些部分。运行时与字符串类似,称为string interning。但是(基于反编译代码)似乎编译器目前没有这样做。

相关问题