Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Struct equality with arrays

If I have arrays in a struct as below, I can't compare the equality of the struct because the arrays are mutable? Is there a way to get the equality to pass down to the array so that I get true for a([1,2,3]) == a([1,2,3])? Or is the only way to do this to extend Base.==?

julia> struct a
       v
       end

julia> a([1,2,3]) == a([1,2,3])
false

julia> a(1) == a(1)
true

julia> [1,2,3] == [1,2,3] # want the equality to work like this for the struct
true

julia> [1,2,3] === [1,2,3]
false
like image 626
Alec Avatar asked Jun 12 '20 02:06

Alec


Video Answer


2 Answers

The answer by @miguel raz does not work at all!

This happens since isequal is actually calling == rather than == calling isequal. In the isequal doc you can find explicitely that:

The default implementation of isequal calls ==, so a type that does not involve floating-point values generally only needs to define ==

Hence the correct code is:

struct A
  v
end
import Base.==
==(x::A,y::A) = x.v==y.v

However, a more elegant approach would be to write a generic code that does not rely on having the field v. Since we do not want to overload the default == operator we can define an abstract type that will tell Julia to use our implementation:

abstract type Comparable end

import Base.==

function ==(a::T, b::T) where T <: Comparable
    f = fieldnames(T)
    getfield.(Ref(a),f) == getfield.(Ref(b),f)
end

Now you can define your own structures that will correctly compare:

struct B <: Comparable
    x
    y
end

Testing:

julia> b1 = B([1,2],[B(7,[1])]);

julia> b2 = B([1,2],[B(7,[1])])

julia> b1 == b2
true
like image 168
Przemyslaw Szufel Avatar answered Oct 13 '22 00:10

Przemyslaw Szufel


As @Przemyslaw answered, an overload of == is needed.
For situations where an abstract class implementation doesn't work, the overload can be done as a one-liner (avoiding the import statement):

Base.:(==)(a::A, b::A) = Base.:(==)(a.v, a.v)

It's good to also override isequal (for structs that don't need special NaN and missing value semantics) and hash (for structs that can be used as keys):

Base.isequal(a::A, b::A) = Base.isequal(a.v, b.v)
Base.hash(a::A, h::UInt) = Base.hash(a.v, h)
like image 23
Jay Lieske Avatar answered Oct 12 '22 23:10

Jay Lieske