|
1 | 1 | module KWayMerges
|
2 | 2 |
|
3 |
| -export my_function, MyType |
| 3 | +export KWayMerger |
| 4 | + |
| 5 | +include("heap.jl") |
4 | 6 |
|
5 | 7 | """
|
6 |
| - MyStruct{T <: Real} |
| 8 | + KWayMerger{F, T, I, S} |
| 9 | +
|
| 10 | +Stateful iterator which does a K-way merge between multiple |
| 11 | +iterators of the same type. |
| 12 | +This iterator will yield the elements in every contained |
| 13 | +iterator. At each iteration, it will choose the element from |
| 14 | +the iterator with the lowest precedence according to the order |
| 15 | +determined by `f::F` (default: `isless`). |
7 | 16 |
|
8 |
| -This struct represents a point in 2D-space. |
| 17 | +If all inner iterators are sorted by `f`, the yielded elements |
| 18 | +will be in sorted order. |
| 19 | +A `KWayMerger` is typically used to combined multiple sorted arrays |
| 20 | +into one sorted array. |
9 | 21 |
|
10 | 22 | # Examples
|
11 | 23 | ```jldoctest
|
12 |
| -julia> x = MyType(1.0, 2.0) |
13 |
| -MyType{Float64}(1.0, 2.0) |
| 24 | +julia> arrs = [[1,4,10], [2,3], [6,8], [5,7,9]]; |
| 25 | +
|
| 26 | +julia> it = KWayMerger(arrs); |
14 | 27 |
|
15 |
| -julia> my_function(x) |
16 |
| -2.23606797749979 |
| 28 | +julia> print(collect(it)) |
| 29 | +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
17 | 30 | ```
|
18 | 31 |
|
19 |
| -See also: [`my_function`](@ref) |
| 32 | +# Extended help |
| 33 | +The type parameters are: |
| 34 | +* `F`: Type of function used to compare the elements. It defaults |
| 35 | + to `typeof(Base.isless)` |
| 36 | +* `T`: Element type of iterators |
| 37 | +* `I`: Iterator type |
| 38 | +* `S`: Type of state of iterators |
| 39 | +
|
| 40 | +All iterators must be of the same type. `Base.eltype` is used |
| 41 | +to determine `T` and `I`; since its default implementation |
| 42 | +returns `Any`, these type parameters might need to be explicitly |
| 43 | +passed to get good performance for some iterators. |
| 44 | +
|
| 45 | +`S` is derived automatically, but this must be a fixed type; |
| 46 | +iterators that use states of multiple different types may |
| 47 | +not be supported by `KWayMerger`. |
20 | 48 | """
|
21 |
| -struct MyType{T <: Real} |
22 |
| - x::T |
23 |
| - y::T |
24 |
| -end |
| 49 | +struct KWayMerger{F, T, I, S} |
| 50 | + f::F |
| 51 | + iterators::Memory{I} |
| 52 | + states::Memory{S} |
| 53 | + heap::Vector{Tuple{T, Int}} |
| 54 | +end |
| 55 | + |
| 56 | +ord(x::KWayMerger) = ord(x.f) |
| 57 | +ord(f) = (i, j) -> f(first(i), first(j)) |
| 58 | + |
| 59 | +collect_to_memory(x) = collect_to_memory(Base.IteratorSize(typeof(x)), x) |
| 60 | + |
| 61 | +function collect_to_memory(::Union{Base.HasShape, Base.HasLength}, x) |
| 62 | + mem = Memory{eltype(x)}(undef, length(x)) |
| 63 | + i = 0 |
| 64 | + for it in x |
| 65 | + i += 1 |
| 66 | + mem[i] = it |
| 67 | + end |
| 68 | + i == length(mem) || error("Implementation error: More elements than reported") |
| 69 | + return mem |
| 70 | +end |
| 71 | + |
| 72 | +function collect_to_memory(::Base.SizeUnknown, x) |
| 73 | + T = eltype(typeof(x)) |
| 74 | + v = collect(T, x) |
| 75 | + return copy!(Memory{T}(undef, length(v)), v) |
| 76 | +end |
| 77 | + |
| 78 | +function KWayMerger{F, T, I}(f::F, iterators) where {F, T, I} |
| 79 | + iters = collect_to_memory(iterators) |
| 80 | + states = nothing |
| 81 | + things = Tuple{T, Int}[] |
| 82 | + for i in eachindex(iters) |
| 83 | + it = iterate(iters[i]) |
| 84 | + isnothing(it) && continue |
| 85 | + (thing::T, state) = it |
| 86 | + if isnothing(states) |
| 87 | + states = Memory{typeof(state)}(undef, length(iters)) |
| 88 | + end |
| 89 | + push!(things, (thing, i)) |
| 90 | + states[i] = state |
| 91 | + end |
| 92 | + heapify!(ord(f), things) |
| 93 | + states = if isnothing(states) |
| 94 | + Memory{Union{}}(undef, length(iters)) |
| 95 | + else |
| 96 | + states |
| 97 | + end |
| 98 | + return KWayMerger{F, T, I, eltype(states)}(f, iters, states, things) |
| 99 | +end |
| 100 | + |
| 101 | +function KWayMerger{T, I}(iterators) where {T, I} |
| 102 | + return KWayMerger{typeof(isless), T, I}(isless, iterators) |
| 103 | +end |
| 104 | + |
| 105 | +KWayMerger(iterators) = KWayMerger(isless, iterators) |
| 106 | + |
| 107 | +function KWayMerger(f::F, iterators) where {F} |
| 108 | + I = eltype(typeof(iterators)) |
| 109 | + T = eltype(I) |
| 110 | + return KWayMerger{F, T, I}(f, iterators) |
| 111 | +end |
| 112 | + |
| 113 | +# We could technically know this, but KWayMerger is stateful, and |
| 114 | +# Julia's iterator length works badly with stateful iterators. |
| 115 | +Base.IteratorSize(::Type{<:KWayMerger}) = Base.SizeUnknown() |
| 116 | +Base.eltype(::Type{<:KWayMerger{F, T}}) where {F, T} = T |
| 117 | + |
| 118 | +function Base.iterate(x::KWayMerger, ::Nothing = nothing) |
| 119 | + isempty(x.heap) && return nothing |
| 120 | + (item, i) = @inbounds x.heap[1] |
| 121 | + iterator = @inbounds x.iterators[i] |
| 122 | + state = @inbounds x.states[i] |
| 123 | + it = iterate(iterator, state) |
| 124 | + if it === nothing |
| 125 | + @inbounds heappop!(ord(x.f), x.heap) |
| 126 | + else |
| 127 | + (new_item, new_state) = it |
| 128 | + @inbounds x.states[i] = new_state |
| 129 | + @inbounds heapreplace!(ord(x.f), x.heap, (new_item, i)) |
| 130 | + end |
| 131 | + return (item, nothing) |
| 132 | +end |
| 133 | + |
| 134 | +Base.isempty(x::KWayMerger) = isempty(x.heap) |
| 135 | +Base.isdone(x::KWayMerger) = isempty(x.heap) |
25 | 136 |
|
26 | 137 | """
|
27 |
| - my_function(x::MyType) -> Real |
| 138 | + peek(x::KWayMerger)::Union{Some, Nothing} |
| 139 | +
|
| 140 | +Get the first element of `x` without advancing the iterator, or `nothing` if the |
| 141 | +iterator is empty. |
28 | 142 |
|
29 |
| -Computes the L2 norm of `x`. |
30 | 143 | # Examples
|
31 | 144 | ```jldoctest
|
32 |
| -julia> x = MyType(1.0, 2.0) |
33 |
| -MyType{Float64}(1.0, 2.0) |
| 145 | +julia> it = KWayMerger([[3, 4], [2, 7]]); |
34 | 146 |
|
35 |
| -julia> my_function(x) |
36 |
| -2.23606797749979 |
37 |
| -``` |
| 147 | +julia> peek(it) |
| 148 | +Some(2) |
38 | 149 |
|
39 |
| -See also: [`MyType`](@ref) |
| 150 | +julia> collect(it); # empty stateful iterator |
| 151 | +
|
| 152 | +julia> peek(it) === nothing |
| 153 | +true |
| 154 | +``` |
40 | 155 | """
|
41 |
| -my_function(x::MyType) = sqrt(x.x^2 + x.y^2) |
| 156 | +function Base.peek(x::KWayMerger{F, T})::Union{Some{T}, Nothing} where {F, T} |
| 157 | + isempty(x.heap) && return nothing |
| 158 | + return @inbounds Some(x.heap[1][1]) |
| 159 | +end |
42 | 160 |
|
43 | 161 | end # module KWayMerges
|
0 commit comments