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..10000000).select(&.even?).map { |x| x * 3 }.take(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..10000000).each.select(&.even?).map { |x| x * 3 }.take(3) #=> #< Iterator(T)::Take...

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..10000000).each.select(&.even?).map { |x| x * 3 }.take(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(@length)
    @produced = 0
  end

  def next
    if @produced < @length
      @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.take(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

Enumerable(T)

Direct including types

Iterator::CompactMap(I, T, U), Matrix::ItemIterator(T), Tuple::ItemIterator(T)

Defined in:

Class Method Summary

Instance Method Summary

Class Method Detail

def self.of(&block : -> T)

def self.of(element : T)

def self.stop

Shortcut for Iterator::Stop::INSTANCE, to signal that there are no more elements in an iterator.


Instance Method Detail

def chain(other : Iterator(U))

def compact_map(&func : T -> U)

def cons(n)

def cycle

def cycle(n : Int)

def each

def each(&block)

def each_slice(n)

def map(&func : T -> U)

abstract def next

Returns the next element in this iterator, or Iterator::Stop::INSTANCE if there are no more elements.


def reject(&func : T -> U)

abstract def rewind

Rewinds the iterator to its original state.


def select(&func : T -> U)

def skip(n)

def skip_while(&func : T -> U)

def slice(n)

def stop

Shortcut for Iterator::Stop::INSTANCE, to signal that there are no more elements in an iterator.


def take(n)

def take_while(&func : T -> U)

def tap(&block : T -> )

def uniq(&func : T -> U)

def uniq

def with_index(offset = 0)

def with_object(obj)

def zip(other : Iterator(U))