如何检查 F# 中的引用相等性?

How do I check for reference equality in F#?

F# 对 = 运算符使用结构相等性,这几乎总是您想要的:

let a = [1; 2; 3]
let b = [1; 2; 3]
printfn "%A" (a = b)  // Prints "true"

但在某些算法中,能够提出问题可能很重要 "Are these two things the same object?" 例如,这有助于检测图中的循环。那么我如何在 F# 中请求引用相等性呢?即,如何编写下面的 isSameObject 函数?

let isSameObject x y = ???
let a = [1; 2; 3]
let b = [1; 2; 3]
let a' = a
printfn "%A" (isSameObject a b)  // Prints "false"
printfn "%A" (isSameObject a a')  // Prints "true"

事实证明,答案是使用 LanguagePrimitives.PhysicalEquality:

let isSameObject = LanguagePrimitives.PhysicalEquality
let a = [1; 2; 3]
let b = [1; 2; 3]
let a' = a
printfn "%A" (isSameObject a b)  // Prints "false"
printfn "%A" (isSameObject a a')  // Prints "true"

我在 Stack Overflow 上找到了一个关于这个问题的问题: short-cutting equality checking in F#? 因为那个问题的主题几乎让我一眼就过去了,我想我会再问(并回答)这个问题。希望这个问题的主题行可以让谷歌搜索 "referential equality in F#".

这样的术语时更容易找到

obj.ReferenceEquals呢?

在评论中,Fyodor Soikin 询问 obj.ReferenceEquals 出了什么问题。答案是 "not much",但是对于大多数 F# 代码来说,LanguagePrimitives.PhysicalEqualityobj.ReferenceEquals 更好的两个方面:

1) PhysicalEquality 在传递两种不同的类型时会抛出编译器错误,而 obj.ReferenceEquals 只需要两个 obj,因此很乐意尝试比较 int listchar list:

let a = [1;2;3]
let b = ['a';'b';'c']

obj.ReferenceEquals(a,b) // Returns false
LanguagePrimitives.PhysicalEquality a b // Compiler error

2) PhysicalEquality 不会让你比较值类型,只能比较引用类型。 obj.ReferenceEquals 将让您比较两个值类型,并隐式地首先将它们装箱。但它会将每个单独装箱,这意味着它会 总是 return false 即使你给它 "same" 值对象:

let n = 3
let n' = n
obj.ReferenceEquals(n,n') // Returns false!
LanguagePrimitives.PhysicalEquality n n' // Compiler error

当然,还有另一个区别,归结为个人喜好和易用性。 PhysicalEquality 采用柯里化风格的参数,这与类型推断和部分应用程序配合得很好。 obj.ReferenceEquals 采用元组式参数,这意味着使用起来稍微难看一些。

由于所有这些原因,LanguagePrimitives.PhysicalEquality几乎 每种情况下都比 obj.ReferenceEquals 更好用。