Существенным отличием в Julia является то, что NamedTuple является его собственным типом, и поэтому компилятор может специализироваться на этой конкретной именованной сигнатуре кортежа, в то время как подход «Словарь» должен искать значение по ключу. Кроме того, каждое значение 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)) ││
5 ┄ return %8
С другой стороны, NamedTuple может иметь стабильный тип. Поскольку поле было известно функции, тип также известен как Float64
.
@code_warntype foo(nt)
Body::Float64
4 1 ─ %1 = (Base.getfield)(x, :k2)::Float64 │╻ getindex
└── return %1