module Iterator(T)
Overview
An Iterator allows processing sequences lazily, as opposed to Enumerable
which processes
sequences eagerly and produces an Array
in most of its methods.
As an example, let's compute the first three numbers in the range 1..10_000_000
that are even,
multiplied by three. One way to do this is:
(1..10_000_000).select(&.even?).map { |x| x * 3 }.first(3) # => [6, 12, 18]
The above works, but creates many intermediate arrays: one for the select call,
one for the map call and one for the take call. A more efficient way is to invoke
Range#each
without a block, which gives us an Iterator so we can process the operations
lazily:
(1..10_000_000).each.select(&.even?).map { |x| x * 3 }.first(3) # => #< Iterator(T)::First...
Iterator redefines many of Enumerable
's method in a lazy way, returning iterators
instead of arrays.
At the end of the call chain we get back a new iterator: we need to consume it, either
using #each
or Enumerable#to_a
:
(1..10_000_000).each.select(&.even?).map { |x| x * 3 }.first(3).to_a # => [6, 12, 18]
To implement an Iterator you need to define a #next
method that must return the next
element in the sequence or Iterator::Stop::INSTANCE
, which signals the end of the sequence
(you can invoke #stop
inside an iterator as a shortcut).
Additionally, an Iterator
can implement #rewind
, which must rewind the iterator to
its initial state. This is needed to implement the #cycle
method.
For example, this is an iterator that returns a sequence of N zeros:
class Zeros
include Iterator(Int32)
def initialize(@size)
@produced = 0
end
def next
if @produced < @size
@produced += 1
0
else
stop
end
end
def rewind
@produced = 0
self
end
end
zeros = Zeros.new(5)
zeros.to_a # => [0, 0, 0, 0, 0]
zeros.rewind
zeros.first(3).to_a # => [0, 0, 0]
The standard library provides iterators for many classes, like Array
, Hash
, Range
, String
and IO
.
Usually to get an iterator you invoke a method that would usually yield elements to a block,
but without passing a block: Array#each
, Array#each_index
, Hash#each
, String#each_char
,
IO#each_line
, etc.
Included Modules
Direct including types
Defined in:
iterator.crClass Method Summary
- .of(&block : -> T)
- .of(element : T)
-
.stop
Shortcut for
Iterator::Stop::INSTANCE
, to signal that there are no more elements in an iterator.
Instance Method Summary
-
#chain(other : Iterator(U))
Returns an iterator that returns elements from the original iterator until it is exhausted and then returns the elements of the second iterator.
-
#compact_map(&func : T -> U)
Return an iterator that applies the given function to the element and then returns it unless it is nil.
-
#cons(n : Int)
Returns an iterator that returns consecutive chunks of the size n.
-
#cycle
Returns an iterator that repeatedly returns the elements of the original iterator forever starting back at the beginning when the end was reached.
-
#cycle(n : Int)
Returns an iterator that repeatedly returns the elements of the original iterator starting back at the beginning when the end was reached, but only n times.
- #each
-
#each(&block)
Calls the given block once for each element, passing that element as a parameter.
-
#each_slice(n)
Returns an iterator that then returns slices of n elements of the initial iterator.
-
#first(n : Int)
Returns an iterator that only returns the first n elements of the initial iterator.
-
#in_groups_of(size : Int, filled_up_with = nil)
Returns an iterator that chunks the iterator's elements in arrays of size filling up the remaining elements if no element remains with nil or a given optional parameter.
-
#map(&func : T -> U)
Returns an iterator that applies the given block to the next element and returns the result.
-
#next
Returns the next element in this iterator, or
Iterator::Stop::INSTANCE
if there are no more elements. -
#reject(&func : T -> U)
Returns an iterator that only returns elements for which the the passed in block returns a falsey value.
-
#rewind
Rewinds the iterator to its original state.
-
#select(&func : T -> U)
Returns an iterator that only returns elements for which the the passed in block returns a truthy value.
-
#skip(n : Int)
Returns an iterator that skips the first n elements and only returns the elements after that.
-
#skip_while(&func : T -> U)
Returns an iterator that only starts to return elements once the given block has returned falsey value for one element.
-
#slice(n : Int)
Returns an iterator that returns slices of n elements of the initial iterator.
-
#step(n : Int)
Returns an iterator that only returns every nth element, starting with the first.
-
#stop
Shortcut for
Iterator::Stop::INSTANCE
, to signal that there are no more elements in an iterator. -
#take_while(&func : T -> U)
Returns an iterator that returns elements while the given block returns a truthy value.
-
#tap(&block : T -> )
Returns an iterator that calls the given block with the next element of the iterator when calling
#next
, still returning the original element. -
#uniq(&func : T -> U)
Returns an iterator that only returns unique values of the original iterator.
-
#uniq
Returns an iterator that only returns unique values of the original iterator.
-
#with_index(offset : Int = 0)
Returns an iterator that returns a tuple of the element and its index.
-
#with_object(obj)
Returns an iterator that returns a tuple of the element and a given object.
-
#zip(other : Iterator(U))
Returns an iterator that returns the elements of this iterator and the given one pairwise as tuples.
Instance methods inherited from module Enumerable(T)
all?all?(&block) all?, any?
any?(&block) any?, compact_map(&block) compact_map, count(item)
count(&block) count, cycle(&block)
cycle(n, &block) cycle, each(&block : T -> _) each, each_cons(count : Int, &block) each_cons, each_slice(count : Int, &block) each_slice, each_with_index(offset = 0, &block) each_with_index, each_with_object(obj, &block) each_with_object, find(if_none = nil, &block) find, first
first(count : Int) first, first? first?, flat_map(&block : T -> Array(U)) flat_map, grep(pattern) grep, group_by(&block : T -> U) group_by, in_groups_of(size : Int, filled_up_with : U = nil, &block)
in_groups_of(size : Int, filled_up_with : U = nil) in_groups_of, includes?(obj) includes?, index(obj)
index(&block) index, index_by(&block : T -> U) index_by, join(separator = "")
join(separator, io, &block)
join(separator = "", &block)
join(separator, io) join, map(&block : T -> U) map, map_with_index(&block : T, Int32 -> U) map_with_index, max max, max? max?, max_by(&block : T -> U) max_by, max_by?(&block : T -> U) max_by?, max_of(&block : T -> U) max_of, max_of?(&block : T -> U) max_of?, min min, min? min?, min_by(&block : T -> U) min_by, min_by?(&block : T -> U) min_by?, min_of(&block : T -> U) min_of, min_of?(&block : T -> U) min_of?, minmax minmax, minmax? minmax?, minmax_by(&block : T -> U) minmax_by, minmax_by?(&block : T -> U) minmax_by?, minmax_of(&block : T -> U) minmax_of, minmax_of?(&block : T -> U) minmax_of?, none?(&block)
none? none?, one?(&block) one?, partition(&block) partition, product(initial : Number, &block)
product
product(initial : Number)
product(&block) product, reduce(memo, &block)
reduce(&block) reduce, reject(&block : T -> ) reject, select(&block : T -> ) select, size size, skip(count : Int) skip, skip_while(&block) skip_while, sum
sum(initial)
sum(&block)
sum(initial, &block) sum, take_while(&block) take_while, to_a to_a, to_h to_h, to_set to_set
Class Method Detail
Shortcut for Iterator::Stop::INSTANCE
, to signal that there are no more elements in an iterator.
Instance Method Detail
Returns an iterator that returns elements from the original iterator until it is exhausted and then returns the elements of the second iterator.
iter = (1..2).each.chain(('a'..'b').each)
iter.next # => 1
iter.next # => 2
iter.next # => 'a'
iter.next # => 'b'
iter.next # => Iterator::Stop::INSTANCE
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.
iter = [1, nil, 2, nil].each.compact_map {|e| e.try &.*(2)}
iter.next # => 2
iter.next # => 4
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that returns consecutive chunks of the size n.
iter = (1..5).each.cons(3)
iter.next # => [1, 2, 3]
iter.next # => [2, 3, 4]
iter.next # => [3, 4, 5]
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that repeatedly returns the elements of the original iterator forever starting back at the beginning when the end was reached.
iter = ["a", "b", "c"].each.cycle
iter.next # => "a"
iter.next # => "b"
iter.next # => "c"
iter.next # => "a"
iter.next # => "b"
iter.next # => "c"
iter.next # => "a"
# and so an and so on
Returns an iterator that repeatedly returns the elements of the original iterator starting back at the beginning when the end was reached, but only n times.
iter = ["a", "b", "c"].each.cycle(2)
iter.next # => "a"
iter.next # => "b"
iter.next # => "c"
iter.next # => "a"
iter.next # => "b"
iter.next # => "c"
iter.next # => Iterator::Stop::INSTANCE
Calls the given block once for each element, passing that element as a parameter.
iter = [ "a", "b", "c" ].each
iter.each {|x| print x, " " } # Prints "a b c"
Returns an iterator that then returns slices of n elements of the initial iterator.
iter = (1..9).each.each_slice(3)
iter.next # => [1, 2, 3]
iter.next # => [4, 5, 6]
iter.next # => [7, 8, 9]
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that only returns the first n elements of the initial iterator.
iter = ["a", "b", "c"].each.first 2
iter.next # => "a"
iter.next # => "b"
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that chunks the iterator's elements in arrays of size filling up the remaining elements if no element remains with nil or a given optional parameter.
iter = (1..3).each.in_groups_of(2)
iter.next # => [1, 2]
iter.next # => [3, nil]
iter.next # => Iterator::Stop::INSTANCE
iter = (1..3).each.in_groups_of(2, 'z')
iter.next # => [1, 2]
iter.next # => [3, 'z']
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that applies the given block to the next element and returns the result.
iter = [1, 2, 3].each.map &.*(2)
iter.next # => 2
iter.next # => 4
iter.next # => 6
iter.next # => Iterator::Stop::INSTANCE
Returns the next element in this iterator, or Iterator::Stop::INSTANCE
if there
are no more elements.
Returns an iterator that only returns elements for which the the passed in block returns a falsey value.
iter = [1, 2, 3].each.reject &.odd?
iter.next # => 2
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that only returns elements for which the the passed in block returns a truthy value.
iter = [1, 2, 3].each.select &.odd?
iter.next # => 1
iter.next # => 3
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that skips the first n elements and only returns the elements after that.
iter = (1..3).each.skip(2)
iter.next # -> 3
iter.next # -> Iterator::Stop::INSTANCE
Returns an iterator that only starts to return elements once the given block has returned falsey value for one element.
iter = [1, 2, 3, 4, 0].each.skip_while { |i| i < 3}
iter.next # => 3
iter.next # => 4
iter.next # => 0
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that returns slices of n elements of the initial iterator.
iter = (1..9).each.slice(3)
iter.next # => [1, 2, 3]
iter.next # => [4, 5, 6]
iter.next # => [7, 8, 9]
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that only returns every nth element, starting with the first.
iter = (1..6).each.step(2)
iter.next # => 1
iter.next # => 3
iter.next # => 5
iter.next # => Iterator::Stop::INSTANCE
Shortcut for Iterator::Stop::INSTANCE
, to signal that there are no more elements in an iterator.
Returns an iterator that returns elements while the given block returns a truthy value.
iter = (1..5).each.take_while {|i| i <3 }
iter.next # => 1
iter.next # => 2
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that calls the given block with the next element of the
iterator when calling #next
, still returning the original element.
a = 0
iter = (1..3).each.tap { |x| a += x}
iter.next # => 1
a # => 1
iter.next # => 2
a # => 3
iter.next # => 3
a # => 6
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that only returns unique values of the original iterator. The provided block is applied to the elements to determine the value to be checked for uniqueness.
iter = [["a", "a"], ["b", "a"], ["a", "c"]].uniq &.first
iter.next # => ["a", "a"]
iter.next # => ["b", "a"]
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that only returns unique values of the original iterator.
iter = [1, 2, 1].each.uniq
iter.next # => 1
iter.next # => 2
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that returns a tuple of the element and its index.
iter = (1..3).each.with_index
iter.next # => {1, 0}
iter.next # => {2, 1}
iter.next # => {3, 2}
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that returns a tuple of the element and a given object.
iter = (1..3).each.with_object("a")
iter.next # => {1, "a"}
iter.next # => {2, "a"}
iter.next # => {3, "a"}
iter.next # => Iterator::Stop::INSTANCE
Returns an iterator that returns the elements of this iterator and the given one pairwise as tuples.
iter1 = [4, 5, 6].each iter2 = [7, 8, 9].each iter = iter1.zip(iter2) iter.next # => {4, 7} iter.next # => {5, 8} iter.next # => {6, 9} iter.next # => Iterator::Stop::INSTANCE