Overload object comparison when adding to a set in Julia?

瘦欲@ 提交于 2020-01-03 17:11:45

问题


Is there a way to overload how Base.Set does its object comparisons in Julia?

I tried overloading isequal and ==, but my objects are still marked as different when they should be the same.

E.g.

type Test
    x
    y
end

function ==(a::Test, b::Test)
    return a.x == b.x && a.y == b.y
end

Set([Test(2,3), Test(2,3)])

gives

Set([Test(2,3),Test(2,3)])

回答1:


A Set is a kind of Dict with values of type Void: ref

type Set{T} <: AbstractSet{T}
    dict::Dict{T,Void}

    Set() = new(Dict{T,Void}())
    Set(itr) = union!(new(Dict{T,Void}()), itr)
end  

And Julia-lang documentation describes Dic type as follow:

Dict is the standard associative collection. Its implementation uses hash() as the hashing function for the key, and isequal() to determine equality. Define these two functions for custom types to override how they are stored in a hash table.

Check, dict.jl, and look for ht_keyindex2() and ht_keyindex() functions. Both will return an index only if these two conditions are true:

if !isslotmissing(h,index) && isequal(key,keys[index])
    return index
end

Where in the above: index = hashindex(key, sz)
Julia uses hash() function do the task of hashing:

hash(x[, h])
Compute an integer hash code such that isequal(x,y) implies hash(x)==hash(y). The optional second argument h is a hash code to be mixed with the result. New types should implement the 2-argument form, typically by calling the 2-argument hash method recursively in order to mix hashes of the contents with each other (and with h). Typically, any type that implements hash should also implement its own == (hence isequal) to guarantee the property mentioned above.

So with these preparations it is obvious that overriding Base.==, is not the right & complete way to do the task, but

  1. We need to override hash() function to return the same hashindex and
  2. Instead of Base.== it is sufficient to override Base.isequal

Code:

type Test
    x
    y
end

Base.hash(a::Test, h::UInt) = hash(a.y, hash(a.x, hash(:Test, h)))
Base.isequal(a::Test, b::Test) = Base.isequal(hash(a), hash(b))
Set([Test(2,3), Test(2,3)])

The point is, although Test(2,3) == Test(2,3) #=> false it works as expected.




回答2:


There is a useful package for composite type comparisons, AutoHashEquals:

using AutoHashEquals

@auto_hash_equals type Test
    x
    y
end

X = Test(2, 3)
Y = Test(2, 3)
Z = Test(1, 3)

X == Y  # = true
X == Z  # = false

Set([X, Y, Z]) # = Set([Test(2,3),Test(1,3)])

However, like @Gnimuc-K pointed out "this macro is only useful for mutable types when they are used as immutable records."



来源:https://stackoverflow.com/questions/34936593/overload-object-comparison-when-adding-to-a-set-in-julia

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!