#struct #julia
#структура #джулия
Вопрос:
Существует поведение, которое я не понимаю при проверке равенства в Julia в отношении объектов «struct». В документации указано :: «Для коллекций ==
обычно вызывается рекурсивно для всего содержимого, хотя другие свойства (например, форма для массивов) также могут быть приняты во внимание«. Хотя кажется, что для структур оно приведено ===
или что-то в этом роде. Вот минимальный рабочий пример :
Как и ожидалось :
string1 = String("S")
string2 = String("S")
string1 == string2
=> возвращает true
и :
set1 = Set(["S"])
set2 = Set(["S"])
set1 == set2
=> возвращает true
НО! И это то, чего я не понимаю :
struct StringStruct
f::String
end
stringstruct1 = StringStruct("S")
stringstruct2 = StringStruct("S")
stringstruct1 == stringstruct2
=> возвращает true
Однако :
struct SetStruct
f::Set{String}
end
setstruct1 = SetStruct(Set(["S"]))
setstruct2 = SetStruct(Set(["S"]))
setstruct1 == setstruct2
=> возвращает false
Для меня это выглядит так, как будто ===
проверяется на элементах структуры.
Итак, мой вопрос: каково реальное поведение ==
при тестировании на структурах? Приводит ли это ==
или ===
? В случае, если он выдает ==
, как указано в документации, в чем смысл, который я неправильно понимаю?
Комментарии:
1. Дополнительная информация: существует проблема, связанная с рекурсивным сравнением структур, как вы упомянули ( github.com/JuliaLang/julia/issues/4648 ). Однако этого не будет ни в одной версии Julia 1.X, так что это будет, по крайней мере, Julia 2.0, прежде чем автоматическая рекурсивная проверка станет частью языка (если это вообще произойдет).
Ответ №1:
Для struct
s по умолчанию ==
возвращается к ===
, так что, например:
setstruct1 == setstruct2
это то же самое, что
setstruct1 === setstruct2
Итак, теперь мы переходим к тому, как ===
это работает. И это определяется как:
Определите
x
, идентичны лиy
и в том смысле, что ни одна программа не может их различить.
(Я опускаю остальную часть определения, поскольку считаю, что это первое предложение создает хорошую ментальную модель).
Теперь ясно stringstruct1
и stringstruct2
не различимо. Они неизменяемы и содержат строки, которые неизменны в Julia. В частности, они имеют одинаковое hash
значение (что не является окончательным тестом, но является хорошей ментальной моделью здесь).
julia> hash(stringstruct1), hash(stringstruct2)
(0x9e0bef39ad32ce56, 0x9e0bef39ad32ce56)
Теперь setstrict1
и setstruct2
различимы. Они хранят разные наборы, хотя на момент сравнения эти наборы содержат одни и те же элементы, но у них разные ячейки памяти (поэтому в будущем они могут быть разными — короче говоря, они различимы). Обратите внимание, что эти структуры, в частности, имеют разные хэши:
julia> hash(setstruct1), hash(setstruct2)
(0xe7d0f90913646f29, 0x3b31ce0af9245c64)
Теперь обратите внимание на следующее:
julia> s = Set(["S"])
Set{String} with 1 element:
"S"
julia> ss1 = SetStruct(s)
SetStruct(Set(["S"]))
julia> ss2 = SetStruct(s)
SetStruct(Set(["S"]))
julia> ss1 == ss2
true
julia> ss1 === ss2
true
julia> hash(ss1), hash(ss2)
(0x9127f7b72f753361, 0x9127f7b72f753361)
На этот раз ss1
и ss2
проходят все тесты, так как они снова неразличимы (если вы измените ss1
, то ss2
изменения будут синхронизированы, поскольку они остаются неизменными Set
).
Комментарии:
1. Отлично! Это полезно, полно и понятно. Спасибо!
Ответ №2:
Хотя ответ Богумила объясняет, что происходит, позвольте мне показать, как привести поведение, которое вы ожидаете, к вашему коду.
Просто добавьте следующие строки.
abstract type Comparable end
import Base.==
==(a::T, b::T) where T <: Comparable =
getfield.(Ref(a),fieldnames(T)) == getfield.(Ref(b),fieldnames(T))
Теперь вы можете сделать свою структуру сопоставимой по своему усмотрению:
struct SetStruct <: Comparable
f::Set{String}
end
setstruct1 = SetStruct(Set(["S"]))
setstruct2 = SetStruct(Set(["S"]))
Тесинг:
julia> setstruct1 == setstruct2
true
Комментарии:
1. Отлично! Это полезно для простого сравнения структур. Спасибо.