使用F#和#x27;GetHashCode()中的哈希函数是什么?
我在网上遇到过几个地方的代码如下所示:使用F#和#x27;GetHashCode()中的哈希函数是什么?,hash,f#,equality,gethashcode,Hash,F#,Equality,Gethashcode,我在网上遇到过几个地方的代码如下所示: [<CustomEquality;NoComparison>] type Test = | Foo | Bar override x.Equals y = match y with | :? Test as y' -> match y' with | Foo -> false | Bar -> tr
[<CustomEquality;NoComparison>]
type Test =
| Foo
| Bar
override x.Equals y =
match y with
| :? Test as y' ->
match y' with
| Foo -> false
| Bar -> true // silly, I know, but not the question here
| _ -> failwith "error" // don't do this at home
override x.GetHashCode() = hash x
我不能轻易地证明它,但它表明,hash x
调用对象上的GetHashCode()
,这意味着上面的代码是危险的。还是只是FSI在渲染
我认为像上面这样的代码只是意味着“请实现自定义等式,但将哈希函数保留为默认值”
同时,我以不同的方式实现了这个模式,但我仍然怀疑我是否正确地假设hash
只调用GetHashCode()
,从而导致一个永恒的循环
另一方面,在FSI中使用equality会立即返回,这表明它要么在比较之前不调用
GetHashCode()
,要么执行其他操作。更新:这在上面的示例中是有意义的。Equals不调用GetHashCode()
,并且equality操作符调用Equals
,而不是GetHashCode()
如果覆盖了GetHashCode()
方法,则将使用该方法:
[code>散列运算符是一个]通用散列函数,用于根据=运算符为相等的项返回相等的散列值。默认情况下,它将对F#并集、记录和元组类型使用结构哈希,对类型的完整内容进行哈希。通过为每个类型实现System.Object.GetHashCode,可以逐个类型调整函数的确切行为
是的,这是一个坏主意,它会导致一个无限循环,这是有道理的。它并不像
hash
函数简单地作为GetHashCode
的包装器那样简单,但我可以轻松地告诉您,使用实现肯定是不安全的:override x.GetHashCode()=hash x
如果通过跟踪散列
函数,您将得到:
您可以在这里看到通配符case调用x.GetHashCode()
,因此很可能会发现自己处于无限递归中
在GetHashCode()
的实现中,我能看到的唯一一种可能需要使用hash
的情况是手动对对象的某些成员进行哈希运算以生成哈希代码
在中有一个(非常古老的)示例,它以这种方式在GetHashCode()
内部使用hash
顺便说一下,这不是你发布的代码中唯一不安全的地方
object.Equals的重写绝对不能引发异常。如果类型不匹配,则返回false。这在System.Object
中有明确的记录
Equals的实现不能抛出异常;他们应该
始终返回一个值。例如,如果obj为null,那么Equals方法
应返回false,而不是引发ArgumentNullException
()“通过为每个类型实现System.Object.GetHashCode,可以逐个类型调整函数的确切行为。”,我也注意到了这一点,但它没有说它将调用GetHashCode()。也就是说,写x.GetHashCode()=如果x<0,那么0或者hash x
(也就是说,如果任何低于零的值都被认为是相等的),这是完全合理的。实际上,我希望hash
调用base.GetHashCode()
,这不会导致无限递归。@Abel它说,您可以通过重写GetHashCode
来更改类型的hash
函数的行为,如果它实际上没有调用GetHashCode()
,它的行为会如何改变?听起来你是在解释这个句子,意思是“你可以在覆盖GetHashCode
时使用hash
函数”,这与它所说的正好相反。不完全一样。我的解释是,它允许特殊情况覆盖,其他情况下默认返回原始情况。但是我明白你的意思。“当你手动散列一个对象的一些成员时”,是的,这实际上是从创建一个可比较的函数类型开始的,类似于string*('T->'U)
,在散列覆盖中,我对字符串调用了hash s
(因此,这里没有无限递归)。但当我在网上看到这些帖子时,我想,嘿,让我们试试吧。。。引出这个问题。感谢您提供的指向源代码的指针。关于你对异常的评论:你是对的,错误的示例代码。。。我在胡闹。@Abel Yep,我想你可能知道这一点,但我认为值得向其他人指出这一点,因为这是一个很容易犯的常见小错误。你可以对我的foo=bar
为真和bar=foo
为假说同样的话,我认为这也是非常糟糕的做法;)。
let foo = Test.Foo;;
hash foo;; // no returning to the console until Ctrl-break
foo.GetHashCode();; // no return
let rec GenericHashParamObj (iec : System.Collections.IEqualityComparer) (x: obj) : int =
match x with
| null -> 0
| (:? System.Array as a) ->
match a with
| :? (obj[]) as oa -> GenericHashObjArray iec oa
| :? (byte[]) as ba -> GenericHashByteArray ba
| :? (int[]) as ba -> GenericHashInt32Array ba
| :? (int64[]) as ba -> GenericHashInt64Array ba
| _ -> GenericHashArbArray iec a
| :? IStructuralEquatable as a ->
a.GetHashCode(iec)
| _ ->
x.GetHashCode()