这的确听起来像一个宏工作:
function unpack(lhs, rhs)
len = length(lhs.args)
if len == 1
# just remove the splatting
l, is_splat = remove_splat(lhs.args[1])
return :($l = $(esc(rhs)))
else
new_lhs = :()
new_rhs = quote
tmp = $(esc(rhs))
$(Expr(:tuple))
end
splatted = false
for (i, e) in enumerate(lhs.args)
l, is_splat = remove_splat(e)
if is_splat
splatted && error("Only one splatting operation allowed on lhs")
splatted = true
r = :(tmp[$i:end-$(len-i)])
elseif splatted
r = :(tmp[end-$(len-i)])
else
r = :(tmp[$i])
end
push!(new_lhs.args, l)
push!(new_rhs.args[4].args, r)
end
return :($new_lhs = $new_rhs)
end
end
remove_splat(e::Symbol) = esc(e), false
function remove_splat(e::Expr)
if e.head == :(...)
return esc(e.args[1]), true
else
return esc(e), false
end
end
macro unpack(expr)
if Meta.isexpr(expr, :(=))
if Meta.isexpr(expr.args[1], :tuple)
return unpack(expr.args[1], expr.args[2])
else
return unpack(:(($(expr.args[1]),)), expr.args[2])
end
else
error("Cannot parse expression")
end
end
它也没有很好的测试,但基本的东西工作:
julia> @unpack head, tail... = [1,2,3,4]
(1,[2,3,4])
julia> @unpack head, middle..., tail = [1,2,3,4,5]
(1,[2,3,4],5)
几个朱莉娅陷阱:
x,y = [1,2,3] #=> x = 1, y = 2
a = rand(3)
a[1:3], y = [1,2,3] #=> a = [1.0,1.0,1.0], y = 2
宏遵循此行为
@unpack a[1:3], y... = [1,2,3]
#=> a=[1.0,1.0,1.0], y=[2,3]
我不认为'head,tail = A [1],A [2:end]'是丑陋的,它明确地告诉'head'和'tail'是什么。如果'A'没有潜在的用法,使用'head,tail = shift!(A),A'会更有效一些。 – Gnimuc
@Gnimuc随着更多项目需要解包,它变得越来越丑陋。 'a,b,c,* d = [1,2,3,4,5]'比使用大量索引或移位! :) –
这听起来像一个宏的工作。 –