如何在Julia 1.0中创建自定义迭代器?

时间:2018-10-17 10:13:31

标签: iterator julia

我在Julia 1.0中具有以下结构:

mutable struct Metadata
    id::Int64
    res_id::Int64
end

这样我就可以创建一个这样的数组,其中id总是递增1,但是res_id有时只是递增,就像这样:

data = [
    Metadata(1, 1),
    Metadata(2, 1),
    Metadata(3, 1),
    Metadata(4, 2),
    Metadata(5, 2),
    Metadata(6, 2),
...]

我想要做的是能够遍历此Array,但是基于res_id(基于res_id的所有数据都是1,然后是2,等等)来获取块。所需的行为将是这样的:

for res in iter_res(data)
    println(res)
end

julia>
[Metadata(1, 1), Metadata(2, 1), Metadata(3, 1)]
[Metadata(4, 2), Metadata(5, 2), Metadata(6, 2)]

考虑到我通常还需要遍历数组以逐个元素地获取数据,我该如何在Julia 1.0中做到这一点?

5 个答案:

答案 0 :(得分:0)

您可以像这样在Generatorfilter中进行迭代:

julia> mutable struct Metadata
           id::Int64
           res_id::Int64
       end

julia> data = [
           Metadata(1, 1),
           Metadata(2, 1),
           Metadata(3, 1),
           Metadata(4, 2),
           Metadata(5, 2),
           Metadata(6, 2),
       ];

julia> for res in (filter(x -> x.res_id == i, data) for i in 1:2)
           println(res)
       end
Metadata[Metadata(1, 1), Metadata(2, 1), Metadata(3, 1)]
Metadata[Metadata(4, 2), Metadata(5, 2), Metadata(6, 2)]

答案 1 :(得分:0)

从变量名看来,您似乎正在从某些计算过程中收集数据。通常,您为此目的使用DataFrame

using DataFrames
data = DataFrame(id=[1,2,3,4,5,6],res_id=[1,1,1,2,2,2])
for group in groupby(data,:res_id)
    println(group)
end

这将产生:

3×2 SubDataFrame{Array{Int64,1}}
│ Row │ id    │ res_id │
│     │ Int64 │ Int64  │
├─────┼───────┼────────┤
│ 1   │ 1     │ 1      │
│ 2   │ 2     │ 1      │
│ 3   │ 3     │ 1      │
3×2 SubDataFrame{Array{Int64,1}}
│ Row │ id    │ res_id │
│     │ Int64 │ Int64  │
├─────┼───────┼────────┤
│ 1   │ 4     │ 2      │
│ 2   │ 5     │ 2      │
│ 3   │ 6     │ 2      │

这对于进一步处理结果也更加方便。

答案 2 :(得分:0)

我最终如何解决该问题:

function iter(data::Vector{Metadata}; property::Symbol = :res_id)

    #GET UNIQUE VALUES FOR THIS PROPERTY
    up = Vector{Any}()
    for s in data
        getproperty(s, property) in up ? nothing : push!(up, getproperty(s, property))
    end

    #GROUP ELEMENTS BASED ON THE UNIQUE VALUES FOR THIS PROPERTY
    f = Vector{Vector{Metadata}}()
    idx::Int64 = 1
    cmp::Any = up[idx]
    push!(f, Vector{Metadata}())
    for s in data
        if getproperty(s, property) == cmp
            push!(f[idx], s)
        else
            push!(f, Vector{Metadata}())
            idx += 1
            cmp = up[idx]
            push!(f[idx], s)
        end
    end
    return f
end

这使我可以容纳“跳过的” res_id(例如从1跳到3等),甚至可以按除res_id之外的其他将来特征(例如字符串)或Int64以外的类型对Metadata对象进行分组。有效,尽管它可能不是很有效。

然后您可以通过以下方式遍历Vector {Metadata}:

for r in iter(rs)
    println(res)
end

答案 3 :(得分:0)

在Julia 1+中,应该通过实现Base.iterate(::YourType)来获得起始迭代,并通过Base.iterate(::YourType, state)来实现基于某些state的其他迭代来实现。完成后,这些方法应返回nothing,否则返回(result, state)元组。

使用... p迭代YourType

for i in x
    # stuff
end

是写作的简写

it = iterate(x)
while it !== nothing
    i, state = it
    # stuff
    it = iterate(x, state)
end

有关详细信息,请参见manual

答案 4 :(得分:-1)

听起来像您需要一个groupBy函数。这是Haskell中供参考的工具

groupBy                 :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _  []           =  []
groupBy eq (x:xs)       =  (x:ys) : groupBy eq zs
                           where (ys,zs) = span (eq x) xs