Julia splat操作员拆包

时间:2017-02-20 02:45:07

标签: julia iterable-unpacking

在Python中,可以在解包迭代中使用*运算符。

In [1]: head, *tail = [1, 2, 3, 4, 5]

In [2]: head
Out[2]: 1

In [3]: tail
Out[3]: [2, 3, 4, 5]

我想在Julia中产生相同的行为。我认为等效的...运算符可以工作,但它似乎只是在这种情况下产生错误。

julia> head, tail... = [1, 2, 3, 4, 5]
ERROR: syntax: invalid assignment location "tail..."

我能够使用以下内容生成我想要的结果,但这是一个难看的解决方案。

julia> head, tail = A[1], A[2:end]
(1,[2,3,4,5])

我可以使用splat(tail)运算符解压缩数组,使head包含...之后的其余项目吗?如果没有,最干净的选择是什么?

修改:此功能已在#2626中提出。看起来它将成为1.0版本的一部分。

1 个答案:

答案 0 :(得分:2)

这确实听起来像一个宏的工作:

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]