Mageek
Mageek

Reputation: 4901

Julia: Dictionary with Tuple keys containing Composite Types

I ran into an issue trying to using a dictionary whose keys are a tuple including a composite type.

Here is a minimal example to replicate my issue:

import Base: hash, isequal    

type T
    a :: Int
    b :: Int
end

function isequal(A::(T,Int), B::(T,Int))
    A[1].a == B[1].a && A[1].b == B[1].b && A[2] == B[2]
end
function hash(A::(T,Int))
    hash(A[1].a + A[1].b + A[2])
end

d = Dict{(T,Int),Int}()

d[(T(1,1),1)] = 1
d[(T(2,2),2)] = 2

r = (T(2,2),2)

for k in keys(d)
    println(isequal(r, k) && hash(r) == hash(k))
end
println(d[r])

Running this results in:

false
true
ERROR: key not found: (T(2,2),2)

So isequal and hash work, but for some reason the dict is not.

Does anyone know what is going on? Thank you.

Upvotes: 2

Views: 1642

Answers (2)

IainDunning
IainDunning

Reputation: 11644

There is something I don't quite understand in this case about the types of tuples and dispatching, but basically you need to implement the two argument form of hash for this case. The following very similar code works as expected, as a point of comparison, without the two argument form:

type T
    a::Int
    b::Int
end

function Base.isequal(A::T, B::T)
    println("isequal", A, " ", B)
    A.a == B.a && A.b == B.b
end
function Base.hash(A::T)
    println("hash", A)
    hash(A.a + A.b)
end

d = Dict{T,Int}()

d[T(1,1)] = 1
d[T(2,2)] = 2

println("test")
r = T(2,2)
println(d[r])

with output

isequalT(1,1) T(1,1)
hashT(1,1)
isequalT(2,2) T(2,2)
hashT(2,2)
test
hashT(2,2)
isequalT(2,2) T(2,2)
2

Upvotes: 1

Mageek
Mageek

Reputation: 4901

The problem can be solved by creating hash & isequal functions for each composite type rather than for the overall tuple.

If you key is (T,Int):

Instead of this:

function isequal(A::(T,Int), B::(T,Int))
    A[1].a == B[1].a && A[1].b == B[1].b && A[2] == B[2]
end
function hash(A::(T,Int))
    hash(A[1].a + A[1].b + A[2])
end

Do this:

function isequal(A::T, B::T)
    A.a == B.a && A.b == B.b
end
function hash(A::T)
    hash(A.a + A.b)
end

If you want the original formulation to work, you have to specify Base.hash with the optional second argument: h::Uint64:

function isequal(A::(T,Int), B::(T,Int))
    A[1].a == B[1].a && A[1].b == B[1].b && A[2] == B[2]
end
function hash(A::(T,Int))
    hash(A[1].a + A[1].b + A[2])
end
function hash(A::(T,Int), h::Uint64)
    hash(A[1].a + A[1].b + A[2] + h)
end

Upvotes: 0

Related Questions