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.