通过别名修改Julia词典



假设我有一本这样的字典:

my_object = Dict{Symbol, Any}(
:foo => Dict{Symbol, Any}(
:foo_items => ["item_a", "item_b", "item_c"],
:bar => Dict{Symbol, Any}(
:bar_name => ["name_a", "name_b", "name_c"],
:type_before => ["Float32", "Float64", "String"],
:type_after => ["Int32", "Int64", "Int8"]
)
)
)

我想转换这些数组,每个数组都有不同的函数,比如使它们成为Symbol而不是String的向量。我可以直接修改这本字典,比如:

# Need to check these keys are present
if haskey(my_object, :foo)
if haskey(my_object[:foo], :foo_items)
my_object[:foo][:foo_items] = Symbol.(my_object[:foo][:foo_items])
...
end

然而,这很快就会变得非常乏味,有很多重复,因此很容易出错。我希望使用别名来使它更简单、更可读,特别是因为像Dict这样的容器是通过引用传递的:

if haskey(my_object, :foo)
foo = my_object[:foo]
if haskey(foo, :foo_items)
foo_items = foo[:foo_items]
foo_items = Symbol.(foo_items)
...
end

但是,在my_object保持不变的情况下,这不起作用。这很奇怪,因为===意味着在进行实际更改之前,内存地址是相同的:

julia> foo = my_object[:foo];
julia> foo === my_object[:foo]
true
julia> foo_items = foo[:foo_items];
julia> foo_items === my_object[:foo][:foo_items]
true

这是copy-on-write的情况吗?为什么我不能这样修改字典?如果我想以更简单的方式更改嵌套词典的元素,我该怎么办?

我会递归

function conversion!(dict, keyset)
for (k, v) in dict
if v isa Dict
conversion!(v, keyset)
else
if k in keyset
dict[k] = converted(Val(k), v)
end
end
end
end
converted(::Val{:foo_items}, value) = Symbol.(value)
# converted(::Val{:bar_name}, value) = ...
my_object = Dict{Symbol, Any}(
:foo => Dict{Symbol, Any}(
:foo_items => ["item_a", "item_b", "item_c"],
:bar => Dict{Symbol, Any}(
:bar_name => ["name_a", "name_b", "name_c"],
:type_before => ["Float32", "Float64", "String"],
:type_after => ["Int32", "Int64", "Int8"]
)
)
)
toconvert = Set([:foo_items])#, :bar_name, :type_before, :type_after])
@show my_object
conversion!(my_object, toconvert)
@show my_object
my_object = Dict{Symbol, Any}(:foo => Dict{Symbol, Any}(:foo_items => ["item_a", "item_b", "item_c"], :bar => Dict{Symbol, Any}(:type_before => ["Float32", "Float64", "String"], :bar_name => ["name_a", "name_b", "name_c"], :type_after => ["Int32", "Int64", "Int8"])))
my_object = Dict{Symbol, Any}(:foo => Dict{Symbol, Any}(:foo_items => [:item_a, :item_b, :item_c], :bar => Dict{Symbol, Any}(:type_before => ["Float32", "Float64", "String"], :bar_name => ["name_a", "name_b", "name_c"], :type_after => ["Int32", "Int64", "Int8"])))

感觉代码可能更稳定。

最新更新