F#等于运算符复杂性

F#等于运算符复杂性,f#,complexity-theory,equals,operator-keyword,user-defined-types,F#,Complexity Theory,Equals,Operator Keyword,User Defined Types,我有一个关于F#中默认的“=”(等于)运算符的问题。它允许比较用户定义的联合类型。问题是:它的复杂性是什么?例如,让我们考虑以下类型: type Tree<'a> = | Nil | Leaf of 'a | Node of Tree<'a> * Tree<'a> 生成此输出: a = b: true a = c: false a = a: true 我预计“a=b”和“a=c”的比较需要线性时间。但是“a=a”呢?如果它是常数,那么更复杂的结

我有一个关于F#中默认的“=”(等于)运算符的问题。它允许比较用户定义的联合类型。问题是:它的复杂性是什么?例如,让我们考虑以下类型:

type Tree<'a> =
  | Nil
  | Leaf of 'a
  | Node of Tree<'a> * Tree<'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:Tree=Node(a,c)
设e:Tree=Node(a,c)

它会经历整个d和e结构,还是会停在“a=a”和“c=c”?

编辑:原始答案是错误的

.Net中
Equals()
的通常实现如下:

  • 通过引用比较这两个实例。如果它们都引用同一对象,则返回
    true
  • 比较两个实例的运行时类型。如果它们不同,则返回
    false
  • 成对比较类型的每个字段是否相等。如果任何值不相等,则返回
    false
    ,否则返回
    true
出于某种原因,F#跳过了第一步,这意味着时间复杂度始终是线性的

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

F#使用结构相等,而.NET中的默认
等于
实现使用引用相等。这意味着,在典型情况下,相等比较是O(N),其中N是要比较的对象图中的字段数

如果要确保
a=a
得到优化,可以先覆盖
Equals
以检查引用是否相等,否则就求助于结构相等。您需要使用
[]
注释您的类型


你可以看到,在中国,结构平等的实施相当漫长。遵循调用层次结构从开始。

我不是下选者,但是您描述的“通常的
实现等于
”不适用于F#union。为什么不呢?它的行为与此完全相同。是的,
Equals(obj)
确实如此,但对于实现
isstructuralequatable
(记录、联合等)的类型,
(=)
调用
Equals(obj、IEqualityComparer)
。它的实现是由编译器提供的,据我所知,它不包括对引用相等性的检查。@Daniel,你说得对。而
Equals(obj)
也不会检查引用的相等性。“我不知道我是怎么忽略这一点的。@布赖恩-有什么原因可以解释为什么F#中的结构等式不是以
ReferenceEquals
开头的吗?”?
a = b: true
a = c: false
a = a: true
let d : Tree<int> = Node (a, c)
let e : Tree<int> = Node (a, c)