我有一个Tuple元素,其中一些是NamedTuples。我想像这样展平NamedTuples:
julia> nt = (a="a", b="b")
(a = "a", b = "b")
julia> t = (1, 2, 3, nt)
(1, 2, 3, (a = "a", b = "b"))
julia> res = tuple(1, 2, 3, nt...)
(1, 2, 3, "a", "b")
Run Code Online (Sandbox Code Playgroud)
如何以编程方式执行此操作?我尝试了以下方法:
julia> exprs = [x isa NamedTuple ? Meta.parse("$x...") : x for x in t]
4-element Array{Any,1}:
1
2
3
:((a = "a", b = "b")...)
julia> res = tuple(eval(ex) for ex in exprs)
(Base.Generator{Array{Any,1},typeof(eval)}(Base.MainInclude.eval, Any[1, 2, 3, :((a = "a", b = "b")...)]),)
Run Code Online (Sandbox Code Playgroud)
但它并没有完全给出我想要的:
(1, 2, 3, "a", "b")
最简单的方法是编写:
julia> Tuple(Iterators.flatten((1,2,3, (a="a",b="b"))))
(1, 2, 3, "a", "b")
Run Code Online (Sandbox Code Playgroud)
这有一个缺点,它是类型不稳定的,它会展平所有可迭代对象(不仅NamedTuple是 s)。
如果你只想NamedTuple变平,那么可以使用这样的东西:
julia> Tuple(reduce((a,b) -> (b isa NamedTuple ? append! : push!)(a, b), (1,2,3, (a="a",b="b")), init=[]))
(1, 2, 3, "a", "b")
Run Code Online (Sandbox Code Playgroud)
(它仍然是类型不稳定的)
如果你想要一些类型稳定的东西,你可以使用递归,例如:
flatten() = ()
flatten(a::NamedTuple) = Tuple(a)
flatten(a) = (a,)
flatten(a::NamedTuple, b...) = tuple(a..., flatten(b...)...)
flatten(a, b...) = tuple(a, flatten(b...)...)
flatten_tuple(x::Tuple) = flatten(x...)
Run Code Online (Sandbox Code Playgroud)
现在你有:
julia> flatten_tuple((1,2,3, (a="a",b="b")))
(1, 2, 3, "a", "b")
julia> @code_warntype flatten_tuple((1,2,3, (a="a",b="b")))
Variables
#self#::Core.Compiler.Const(flatten_tuple, false)
x::Tuple{Int64,Int64,Int64,NamedTuple{(:a, :b),Tuple{String,String}}}
Body::Tuple{Int64,Int64,Int64,String,String}
1 ? %1 = Core._apply_iterate(Base.iterate, Main.flatten, x)::Tuple{Int64,Int64,Int64,String,String}
??? return %1
Run Code Online (Sandbox Code Playgroud)