Возвращение новой структуры с полями, измененными в Юлии - PullRequest
2 голосов
/ 29 октября 2019

У меня проблема с попыткой создать структуру, которая является новой копией существующей структуры с измененными конкретными полями. Я знаю, что, возможно, этого можно достичь с помощью метапрограммирования. Однако будет ли это правильным подходом или я заново изобрету колесо?

Например:

struct A
  a
  b
end

var = A(1,2)
var.b  = 4 # This means var = A(1,4)

Ответы [ 3 ]

2 голосов
/ 29 октября 2019

Проверьте Setfield.jl , это пакет, чтобы сделать именно это.

1 голос
/ 29 октября 2019

Обычно я определяю struct с помощью Base.@kwdef, а затем определяю внешний конструктор, например

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

, а затем вы можете сделать

var = A(1, 2)
var2 = A(var, b = 4)

, вы также можете определитьsetindex! чтобы получить синтаксис, который вы имели в OP, но это позволило бы вам изменять только одно поле за раз.

1 голос
/ 29 октября 2019

Я не уверен, что уже есть что-то похожее или лучше реализованное, но это работает и не зависит от метапрограммирования:

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)
...