我有一个递归函数,它利用全局字典来存储遍历树时已经获得的值。但是,至少存储在dict中的一些值似乎消失了!这个简化的代码显示了问题:
type id
level::Int32
x::Int32
end
Vdict = Dict{id,Float64}()
function getV(w::id)
if haskey(Vdict,w)
return Vdict[w]
end
if w.level == 12
return 1.0
end
w.x == -111 && println("dont have: ",w)
local vv = 0.0
for j = -15:15
local wj = id(w.level+1,w.x+j)
vv += getV(wj)
end
Vdict[w] = vv
w.x == -111 && println("just stored: ",w)
vv
end
getV(id(0,0))
输出有很多这样的行:
just stored: id(11,-111)
dont have: id(11,-111)
just stored: id(11,-111)
dont have: id(11,-111)
just stored: id(11,-111)
dont have: id(11,-111)
...
我有一个愚蠢的错误,或者朱莉娅的咒语中是否有错误?
答案 0 :(得分:6)
默认情况下,自定义类型具有相等的实现和按对象标识散列。由于您的id
类型是可变的,因此Julia是保守的,并假设您关心区分每个实例(因为它们可能会分歧):
julia> type Id # There's a strong convention to capitalize type names in Julia
level::Int32
x::Int32
end
julia> x = Id(11, -111)
y = Id(11, -111)
x == y
false
julia> x.level = 12; (x,y)
(Id(12,-111),Id(11,-111))
朱莉娅并不知道你是否关心对象的长期行为或其当前价值。
有两种方法可以让您按照自己的喜好行事:
使您的自定义类型不可变。您似乎不需要改变Id
的内容。解决此问题的最简单,最直接的方法是将其定义为immutable Id
。现在Id(11, -111)
与Id(11, -111)
的任何其他结构完全无法区分,因为它的值永远不会改变。作为奖励,你也可以看到更好的表现。
如果您确实需要改变值,您可以选择定义自己的==
和Base.hash
实现,以便他们只关心当前值:
==(a::Id, b::Id) = a.level == b.level && a.x == b.x
Base.hash(a::Id, h::Uint) = hash(a.level, hash(a.x, h))
作为@StefanKarpinski just pointed out on the mailing list,这不是可变值&#34;的默认值,因为它可以很容易地在dict中粘贴某些内容,然后将其变异,并且&#39丢失它&#39; <#em>也就是说,对象的哈希值已经改变,但字典根据其旧的哈希值将其存储在一个地方,现在你再也无法访问了通过键查找键/值对。即使你创建了第二个具有相同原始属性的对象,它也无法找到它,因为字典在找到哈希匹配后检查相等性。查找该密钥的唯一方法是将其变回原始值或明确要求字典Base.rehash!
其内容。
在这种情况下,我强烈推荐选项1。