Julia 命名元组的Setindex

Julia 命名元组的Setindex,julia,namedtuple,Julia,Namedtuple,我正在尝试为命名元组编写一个非变异的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 但我的主要问题是,我不确定迭代命名元

我正在尝试为命名元组编写一个非变异的
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

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

您可以使用
,它为键值对提供迭代器,或者使用
字段名
类型
T
并使用
nt[name]
访问值,或者(对命名的tuple变量使用
),我认为您更喜欢

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


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

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

出于好奇,函数中的参数数量是否正确?如果您已经有
nt
(例如
function setindex(x::T,y,v::Val{var}),那么
x
有什么作用
?我遗漏了什么吗?是的,那是个错误。这个实现唯一的问题是它的类型不稳定。我认为在某些情况下,在这里使用
@生成的
函数可能是一个更好的解决方案,或者至少在函数的返回值中添加一个
::T
类型注释,这将覆盖类型不稳定。@BogumiłKamiński为什么会这样?指定返回类型将如何解决此问题?如果
,则必须在
的两个分支中返回T。尝试在命名元组
上的代码上调用
@code\u warntype
(a=1,b=“2”,c='3',d=nothing)
您会发现有一个问题。如果在
NamedTuple
中有4个或4个以上的唯一类型,Julia将无法提供正确的类型(此限制是“内置”的,以提高编译器的性能)。作为旁注,我在Julia中实现ABMs时也遇到了同样的问题。请参阅“几种类型的代理”一节,以及(我没有检查是否与
NamedTuple
相同,原因也是
tmerge
,但很可能是同一个问题)。
nt=(a=2,b=5);(nt…,b=4)
。哇,我从来没有发现过有效的新方法。这让问题变得简单多了!很好!
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