Skip to content

Commit

Permalink
Iterator could chain indetermined number of iterators now
Browse files Browse the repository at this point in the history
  • Loading branch information
xqyww123 committed Aug 20, 2018
1 parent d37fdff commit 11c9934
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
49 changes: 49 additions & 0 deletions spec/std/iterator_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,55 @@ describe Iterator do
iter.rewind
iter.to_a.should eq([1, 2, 'a', 'b'])
end
describe "chain indeterminate number of iterators" do
it "chains all together" do
n = 10 + rand 10
arrs = [] of Array(Int32)
n.times {|a| arrs << [a, a*a, a+3] }
iter = Iterator(Int32).chain arrs.map(&.each)
n.times { |a|
iter.next.should eq a
iter.next.should eq a*a
iter.next.should eq a+3
}
end
it "chains empty" do
arrs = [] of Array(Int32)
iter = Iterator(Int32).chain arrs.map(&.each)
iter.next.should be_a Iterator::Stop
end
it "chains array of empty" do
n = 10 + rand 10
arrs = [] of Array(Int32)
n.times {|a| arrs << (a % 3 == 0 ? [] of Int32 : [a, a*a, a+3]) }
iter = Iterator(Int32).chain arrs.map(&.each)
n.times { |a|
next if a % 3 == 0
iter.next.should eq a
iter.next.should eq a*a
iter.next.should eq a+3
}
end
it "rewinds" do
n = 10 + rand 10
arrs = [] of Array(Int32)
n.times {|a| arrs << (a % 3 == 0 ? [] of Int32 : [a, a*a, a+3]) }
iter = Iterator(Int32).chain arrs.map(&.each)
n.times { |a|
next if a % 3 == 0
iter.next.should eq a
iter.next.should eq a*a
iter.next.should eq a+3
}
iter.rewind
n.times { |a|
next if a % 3 == 0
iter.next.should eq a
iter.next.should eq a*a
iter.next.should eq a+3
}
end
end
end

describe "compact_map" do
Expand Down
50 changes: 50 additions & 0 deletions src/iterator.cr
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,56 @@ module Iterator(T)
end
end

# Like `#chain` but could chains multiple iterators.
# In case number of iterators cannot be determined or depends on input,
# such as chaining an array of iterator.
# `#chain(other)` concact two iterator in linear,
# but in this case, `#chain(other)` with a loop cannot be expanded in linear,
# and compiler will traps in recursive expansion.
# This method uses a loop to iterate all of them, to avoid the recursive.
#
# ```
# array_of_iters = [[1],[2,3],[4,5,6]]
# iter = Iterator(Int32).chain array_of_iters
# iter.next # => 1
# iter.next # => 2
# iter.next # => 3
# iter.next # => 4
# ```
def self.chain(iters : Iterator(Iter)) forall Iter
ChainsAll(Iter, T).new iters
end
# the same as `.chain(Iterator(Iter))`
def self.chain(iters : Iterable(Iter)) forall Iter
chain iters.each
end

private class ChainsAll(Iter, T)
include Iterator(T)
@iterators : Iterator(Iter)
@current : Iterator(T) | Stop
def initialize(@iterators)
@current = @iterators.next
end
def rewind
@iterators.rewind
@iterators.each &.rewind
@iterators.rewind
@current = @iterators.next
self
end
def next : T | Stop
return Stop::INSTANCE if (c = @current).is_a? Stop
ret = c.next
while ret.is_a? Stop
c = @current = @iterators.next
return Stop::INSTANCE if c.is_a? Stop
ret = c.next
end
ret
end
end

# Return an iterator that applies the given function to the element and then
# returns it unless it is `nil`. If the returned value would be `nil` it instead
# returns the next non `nil` value.
Expand Down

0 comments on commit 11c9934

Please sign in to comment.