構造体の分解
構造体を分解するには、構造体名の取得、各フィールド名の取得、各フィールドの取得が必要。
構造体名は、typeof
で取得できる。
julia> struct A
x
y
end
julia> a = A(1,2)
A(1, 2)
julia> println(typeof(a))
A
フィールド名はfieldnames
で取得。
julia> fieldnames(A)
(:x, :y)
各フィールドの値はgetproperty
で取得。
julia> getproperty(a, :x)
1
まとめると以下の関数で分解することができる。
# returns type and vals
function decompose(target)
t = typeof(target)
vals = [getproperty(target, name) for name in fieldnames(t)]
t, vals
end
構造体の合成
構造体の合成はコンストラクタを使えばできるように思える。
t(vals...)
しかしこれは、コンストラクタが明示的に実装されているとうまく行かない。
julia> struct B
x
y
B(x) = new(x, 1)
end
julia> b = B(1)
B(1, 1)
julia> t, vals = decompose(b)
(B, [1, 1])
julia> t(vals...)
ERROR: MethodError: no method matching B(::Int64, ::Int64)
Closest candidates are:
B(::Any) at REPL[21]:4
明示的に1引数のコンストラクタが作られたので、デフォルトの2引数のコンストラクタが見えなくなっている。
これを回避する方法を探したらこんな方法を見つけた。
@generated function __new__(T, args...)
return Expr(:splatnew, :T, :args)
end
function compose(t, vals)
__new__(t, vals...)
end
正直言ってなんでこれでうまくいくのか完全に腹落ちしていないのだが、
これだとうまくいく。
julia> compose(t, vals)
B(1, 1)
なおこの方法だと、引数の数が間違っていた場合に、エラーも出さずに失敗するので要注意。