-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Description
problem statement:
a common idiom when working with Associative objects is to do a set of has / insert / lookup operations:
d = Dict()
if !(key in keys(d))
d[key] = initial_value
end
value = d[key]this syntax has the advantage of only using basic operations, for clarity, but it's also 3x slower than necessary since the dict lookup gets repeated three times. that concern has lead to the introduction of a get! method that accepts either an object or function, to insert a value if the lookup fails, so that the above code can be rewritten as:
d = Dict()
value = get!(d, key, initial_value)or
d = Dict()
value = get!(d, key, () -> initial_value)however, this implementation is still not necessarily faster, since it involves precomputing initial_value (fine it it's just a constant, but bad if it expensive to compute or creates unnecessary garbage) or invoking a lambda.
the code to support this one code pattern isn't exactly short, requires a fair amount of code duplication, and even a macro definition is provided (https://github.com/JuliaLang/julia/blob/master/base/dict.jl#L632-L690)
proposal
define the Ref operation on a Dict to return an intelligent indexer object. the above code pattern could then be written as:
d = Dict()
bp = Ref(d, key)
if isempty(bp)
bp[] = initial_value
end
value = bp[]implementation sketch
type RefDict{K, V, D <: Dict} <: Ref{V}
d::D
k::K
last_htindex::Int
last_state::Int
end
function RefDict{K,V}(d::Dict{K,V}, k)
key = convert(K, k)
isequal(key, k) || throw(ArgumentError("k is not a usable key"))
return RefDict{K,V,typeof(d)}(d, k, 0, 0)
end
function isempty(r::RefDict)
if (d.last_state != d.changecounter)
r.last_htindex = ht_keyindex2(d, key)
r.last_state = d.changecounter
end
return r.last_htindex > 0
endref #12035 (comment)