字典与 NamedTuples
Dictionaries vs NamedTuples
除了 Dictionaries 是可变的而 NamedTuple 不是,NamedTuple 可以通过位置和一些不同的符号来检索,Julia 中的 Dictionaries 和 NamedTuples 之间是否存在 其他 显着差异?何时使用其中之一?
它们看起来很相似:
# Definition
d = Dict("k1"=>"v1", "k2"=>"v2")
nt = (k1="v1", k2="v2")
# Selection by specific key
d["k1"]
nt.k1
# Keys
keys(d)
keys(nt)
# Values
values(d)
values(nt)
# Selection by position
d[1] # error
nt[1]
根据关于 Python 的类似问题,似乎.. 不是。主要区别似乎是 mutable/immutable 属性 并且 x.a 比 x[a]...:[=13=] 更易读且更简洁
将 NamedTuple
视为 Julia 中的匿名 struct
而不是 Dict
。特别是在 NamedTuple
中存储异构类型是类型稳定的。
注意,这也是Python和Julia在思维上的一大区别。在 Julia 中,如果你希望你的代码更快,你通常会关心类型推断。
Julia 的一个显着差异是 NamedTuple 是它自己的类型,因此编译器可以专注于特定的命名元组签名,而 Dictionary 方法必须从键中查找值。此外,NamedTuple 的每个值本身都可以是不同的类型,从而允许进一步优化和类型稳定性,这超出了字典所能实现的范围。如果我们再多改一点,让字典的类型是 Heterogeneous 的,那么它的类型是 Dict{Symbol,Any}
,你可以看到它仍然是稳定的类型。
d=Dict(:k1=>"v1",:k2=>2.0)
nt=(k1="v1",k2=2.0)
foo(x) = x[:k2]
现在如果一个函数直接使用这个字典或者命名元组,我们可以看到对于字典类型,结果是类型不稳定的,因为Dictionary中的值只能通过[=14的类型来保证=].
@code_warntype foo(d)
Body::Any
4 1 ─ %1 = invoke Base.ht_keyindex(_2::Dict{Symbol,Any}, :k2::Symbol)::Int64 │╻ getindex
│ %2 = (Base.slt_int)(%1, 0)::Bool ││╻ <
└── goto #3 if not %2 ││
2 ─ %4 = %new(Base.KeyError, :k2)::KeyError ││╻ Type
│ (Base.throw)(%4) ││
└── $(Expr(:unreachable)) ││
3 ─ %7 = (Base.getfield)(x, :vals)::Array{Any,1} ││╻ getproperty
│ %8 = (Base.arrayref)(false, %7, %1)::Any ││╻ getindex
└── goto #5 ││
4 ─ $(Expr(:unreachable)) ││
5 ┄ return %8
另一方面,NamedTuple 可以是类型稳定的。因为函数已知该字段,所以该类型也已知为 Float64
.
@code_warntype foo(nt)
Body::Float64
4 1 ─ %1 = (Base.getfield)(x, :k2)::Float64 │╻ getindex
└── return %1
除了 Dictionaries 是可变的而 NamedTuple 不是,NamedTuple 可以通过位置和一些不同的符号来检索,Julia 中的 Dictionaries 和 NamedTuples 之间是否存在 其他 显着差异?何时使用其中之一?
它们看起来很相似:
# Definition
d = Dict("k1"=>"v1", "k2"=>"v2")
nt = (k1="v1", k2="v2")
# Selection by specific key
d["k1"]
nt.k1
# Keys
keys(d)
keys(nt)
# Values
values(d)
values(nt)
# Selection by position
d[1] # error
nt[1]
根据关于 Python 的类似问题,似乎.. 不是。主要区别似乎是 mutable/immutable 属性 并且 x.a 比 x[a]...:[=13=] 更易读且更简洁
将 NamedTuple
视为 Julia 中的匿名 struct
而不是 Dict
。特别是在 NamedTuple
中存储异构类型是类型稳定的。
注意,这也是Python和Julia在思维上的一大区别。在 Julia 中,如果你希望你的代码更快,你通常会关心类型推断。
Julia 的一个显着差异是 NamedTuple 是它自己的类型,因此编译器可以专注于特定的命名元组签名,而 Dictionary 方法必须从键中查找值。此外,NamedTuple 的每个值本身都可以是不同的类型,从而允许进一步优化和类型稳定性,这超出了字典所能实现的范围。如果我们再多改一点,让字典的类型是 Heterogeneous 的,那么它的类型是 Dict{Symbol,Any}
,你可以看到它仍然是稳定的类型。
d=Dict(:k1=>"v1",:k2=>2.0)
nt=(k1="v1",k2=2.0)
foo(x) = x[:k2]
现在如果一个函数直接使用这个字典或者命名元组,我们可以看到对于字典类型,结果是类型不稳定的,因为Dictionary中的值只能通过[=14的类型来保证=].
@code_warntype foo(d)
Body::Any
4 1 ─ %1 = invoke Base.ht_keyindex(_2::Dict{Symbol,Any}, :k2::Symbol)::Int64 │╻ getindex
│ %2 = (Base.slt_int)(%1, 0)::Bool ││╻ <
└── goto #3 if not %2 ││
2 ─ %4 = %new(Base.KeyError, :k2)::KeyError ││╻ Type
│ (Base.throw)(%4) ││
└── $(Expr(:unreachable)) ││
3 ─ %7 = (Base.getfield)(x, :vals)::Array{Any,1} ││╻ getproperty
│ %8 = (Base.arrayref)(false, %7, %1)::Any ││╻ getindex
└── goto #5 ││
4 ─ $(Expr(:unreachable)) ││
5 ┄ return %8
另一方面,NamedTuple 可以是类型稳定的。因为函数已知该字段,所以该类型也已知为 Float64
.
@code_warntype foo(nt)
Body::Float64
4 1 ─ %1 = (Base.getfield)(x, :k2)::Float64 │╻ getindex
└── return %1