字典 vs 命名元组

4
除了字典可变而命名元组不可变、命名元组可以通过位置检索并具有稍微不同的表示法之外,在Julia中,字典和命名元组还有其他显著的区别吗?何时使用其中之一?
它们看起来非常相似:
# 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]

一个重要的区别是,NamedTuple 中键/值的顺序很重要。即使 Dict(:a => 1, :b => 2) == Dict(:b => 2, :a => 1)(a=1,b=2) != (b=2,a=1) - BallpointBen
3个回答

11
在Julia中,一个显著的差异是NamedTuple是它自己的类型,因此编译器可以专门针对特定的命名元组签名进行优化,而使用Dictionary方法必须从键中查找该值。此外,NamedTuple的每个值本身可以是不同的类型,允许进一步优化和类型稳定性,超出了字典所能达到的范围。如果我们将字典的类型更改为异构的,以便它是Dict{Symbol,Any}类型,您可以看到它如何仍然保持类型稳定。
d=Dict(:k1=>"v1",:k2=>2.0)
nt=(k1="v1",k2=2.0)

foo(x) = x[:k2]

现在,如果一个函数直接使用这个字典或命名元组,我们可以看到对于字典类型,结果是不稳定的,因为字典中的值只能保证是 Any 类型。

@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))                                                                                                                                                                                           ││
  5return %8

另一方面,NamedTuple 可以是类型稳定的。由于函数已知道该字段,因此该类型也被确定为 Float64

@code_warntype foo(nt)

Body::Float64
4 1 ─ %1 = (Base.getfield)(x, :k2)::Float64                                                                                                                                                                                     │╻ getindex
  └──      return %1

7

NamedTuple看作是Julia中的匿名struct而不是Dict。特别地,在NamedTuple中存储异构类型是类型稳定的。

请注意,这也是Python和Julia之间思考方式的一个重要区别。在Julia中,如果您希望代码运行速度快,通常需要关注类型推断。


-5

1
所以,这个“答案”肯定是错误的。我保留它,因为它是一个很好的提醒,Python和Julia之间存在差异;-) - Antonello

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接