J'ai ce problème d'essayer de créer une structure qui est une nouvelle copie d'une structure existante avec des champs spécifiques modifiés. Je sais qu'il est probablement possible d'y parvenir grâce à la métaprogrammation. Cependant, est-ce que ce serait la bonne approche ici ou est-ce que je réinventerais la roue?
Par exemple:
struct A a b end var = A(1,2) var.b = 4 # This means var = A(1,4)
3 Réponses :
Je ne sais pas s'il existe déjà quelque chose de similaire ou de mieux implémenté, mais cela fonctionne et ne repose sur aucune métaprogrammation:
struct A
a
b
end
var = A(1,2)
function reinstantiate(old, pairs::Pair...)
T = typeof(old)
field_values = [getfield(old, field) for field in fieldnames(T)]
for pair in pairs
index = findfirst(fieldnames(T) .== pair.first)
@assert index != nothing "$(pair.first) is not a field of $(T)"
field_values[index] = pair.second
end
return T(field_values...)
end
var2 = reinstantiate(var, :a=>3, :b=>4)
Je vais généralement définir la struct avec Base. @ kwdef puis définir un constructeur externe comme
var = A(1, 2) var2 = A(var, b = 4)
et ensuite vous pouvez faire
Base.@kwdef struct A
a
b
end
Base.convert( ::Type{NamedTuple}, a::A ) = NamedTuple{propertynames(a)}(a)
function A( a::A; kwargs... )
nt = convert(NamedTuple, a)
nt = merge( nt, kwargs.data )
return A(;nt...)
end
vous pouvez également définir setindex! pour obtenir la syntaxe que vous aviez dans l'OP, mais cela ne vous permettrait de changer qu'un champ à une fois.
Je ne pense pas que cela fonctionne hors de la boîte, sur Julia 1.0.4 et Julia 1.4.0: MethodError: pas de méthode correspondant à length (:: Person) . Cela dépend-il éventuellement de l'installation de NamedTupleTools?
vous avez raison. Le constructeur NamedTuple attend un itérable. Il suffirait de définir Base.collect pour le nouveau type si vous ne voulez pas implémenter l'interface complète de l'itérateur.