2
votes

Retour d'une nouvelle structure avec des champs modifiés dans Julia

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)


0 commentaires

3 Réponses :


1
votes

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)


0 commentaires

2
votes

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.


2 commentaires

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.



2
votes

Vérifiez Setfield.jl , c'est un package pour faire exactement cela.


0 commentaires