命名元组的 Setindex

Setindex for named tuple

我正在尝试为命名元组编写一个非变异 setindex,其中给定名称 var 的值类型,它会在 x.var = y 处创建一个新的命名元组。我现在拥有的是:

function setindex(nt::T,x,v::Val{var}) where {T,var}
  if var ∉ fieldnames(T)
    return x
  else
    typeof(x)(s == var ? y : w for (s,w) in nt)
  end
end

但我的主要问题是我不确定迭代命名元组以获得 (name,value) 对的好方法。

您可以使用 pairs 为键值对提供迭代器,或者使用类型为 Tfieldnames 并使用 nt[name] 或(使用keys 与命名的元组变量以相同的方式)。我想你更喜欢 pairs.

function setindex(nt::T,y,v::Val{var}) where {T,var}
  if var ∉ fieldnames(T)
    return nt
  else
    T(s == var ? y : w for (s,w) in pairs(nt))
  end
end

function setindex(nt::T,y,v::Val{var}) where {T,var}
  if var ∉ fieldnames(T)
    return nt
  else
    T(k == var ? y : nt[k] for k in fieldnames(T))
  end
end

这是@generated版本。生成的代码非常简单并且类型稳定。

julia> @generated function setindex(x::NamedTuple,y,v::Val)
         k = first(v.parameters)
         k ∉ x.names ? :x : :( (x..., $k=y) )
       end

julia> @code_warntype setindex((a=2, b=3), 4, Val(:b))
Body::NamedTuple{(:a, :b),Tuple{Int64,Int64}}
2 1 ─ %1 = (Base.getfield)(x, :a)::Int64                                                               │╻╷╷   macro expansion
  │   %2 = %new(NamedTuple{(:a, :b),Tuple{Int64,Int64}}, %1, y)::NamedTuple{(:a, :b),Tuple{Int64,Int64}}│┃││╷  merge
  └──      return %2                                                                                   ││    

julia> @code_warntype setindex((a=2, b=3), 4, Val(:c))
Body::NamedTuple{(:a, :b),Tuple{Int64,Int64}}
2 1 ─     return x