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

rmu*_*unn 24 f# equality

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

let a = [1; 2; 3]
let b = [1; 2; 3]
printfn "%A" (a = b)  // Prints "true"
Run Code Online (Sandbox Code Playgroud)

但是在某些算法中,能够问"这两件事是同一个对象吗?"非常重要.例如,这可以帮助检测图中的循环.那么我如何在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"
Run Code Online (Sandbox Code Playgroud)

rmu*_*unn 42

事实证明,答案是使用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"
Run Code Online (Sandbox Code Playgroud)

我可以在Stack Overflow上找到一个问题: F#中的短切平等检查?因为那个问题的主题几乎让我一眼就看过它,我想我会再问(并回答)这个问题.希望这个问题的主题将更容易找到谷歌搜索"F#中的参考平等"等术语.

怎么样obj.ReferenceEquals

在评论中,Fyodor Soikin询问有什么问题obj.ReferenceEquals.答案是"不多",但有两种方法LanguagePrimitives.PhysicalEqualityobj.ReferenceEquals大多数F#代码更好:

1)PhysicalEquality抛出一个编译器错误,当你传递两个不同类型的,而obj.ReferenceEquals仅仅需要两个objS和因此愉快地尝试将比较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
Run Code Online (Sandbox Code Playgroud)

2)PhysicalEquality不会让你比较值类型,只有引用类型.obj.ReferenceEquals将允许您比较两个值类型,并将隐式地将它们打包.但是它将每个单独分开,这意味着即使你给它"相同"的值对象它也总是返回false:

let n = 3
let n' = n
obj.ReferenceEquals(n,n') // Returns false!
LanguagePrimitives.PhysicalEquality n n' // Compiler error
Run Code Online (Sandbox Code Playgroud)

当然,还有另一个不同之处,可归结为个人偏好和易用性.PhysicalEquality采用curried风格的参数,可以很好地与类型推断和部分应用程序配合使用.obj.ReferenceEquals采用tupled风格的参数,这意味着它使用起来有点难看.

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

  • 好老``obj.ReferenceEquals`出了什么问题? (4认同)
  • @FyodorSoikin-我针对您的问题写了两条评论,然后意识到,使用代码示例,它们会更好地回答。这样就可以了:obj.ReferenceEquals并不坏,但是PhysicalEquality更好,因为如果您尝试比较两种不同的类型,它将给您键入错误。(几乎可以肯定这是代码中的错误,并且您*希望*编译器帮助您捕获它)。 (2认同)