如何检查 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.PhysicalEquality
比 obj.ReferenceEquals
更好的两个方面:
1) PhysicalEquality
在传递两种不同的类型时会抛出编译器错误,而 obj.ReferenceEquals
只需要两个 obj
,因此很乐意尝试比较 int list
到 char 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
更好用。
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.PhysicalEquality
比 obj.ReferenceEquals
更好的两个方面:
1) PhysicalEquality
在传递两种不同的类型时会抛出编译器错误,而 obj.ReferenceEquals
只需要两个 obj
,因此很乐意尝试比较 int list
到 char 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
更好用。