字典与 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