Iterator

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 : Int32)
    @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.html?lang=en#each, Array.html?lang=en#each_index, Hash#each, String#each_char, IO#each_line, etc.

Included Modules

Defined in:

iterator.cr

Class Method Summary

Instance Method Summary

Instance methods inherited from module Enumerable(T)

all?(&block)
all? all?
, any?(&block)
any? any?
, chunks(&block : T -> U) forall U chunks, compact_map(&block) compact_map, count(&block)
count(item) count
, cycle(n, &block)
cycle(&block) cycle
, each(&block : T -> _) each, each_cons(count : Int, reuse = false, &block) each_cons, each_slice(count : Int, reuse = false, &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(count : Int)
first first
, first? first?, flat_map(&block : T -> Array(U) | Iterator(U) | U) forall U flat_map, grep(pattern) grep, group_by(&block : T -> U) forall U group_by, in_groups_of(size : Int, filled_up_with : U = nil) forall U
in_groups_of(size : Int, filled_up_with : U = nil, reuse = false, &block) forall U in_groups_of
, includes?(obj) includes?, index(&block)
index(obj) index
, index_by(&block : T -> U) forall U index_by, join(separator, io)
join(separator = "")
join(separator, io, &block)
join(separator = "", &block) join
, map(&block : T -> U) forall U map, map_with_index(&block : T, Int32 -> U) forall U map_with_index, max max, max? max?, max_by(&block : T -> U) forall U max_by, max_by?(&block : T -> U) forall U max_by?, max_of(&block : T -> U) forall U max_of, max_of?(&block : T -> U) forall U max_of?, min min, min? min?, min_by(&block : T -> U) forall U min_by, min_by?(&block : T -> U) forall U min_by?, min_of(&block : T -> U) forall U min_of, min_of?(&block : T -> U) forall U min_of?, minmax minmax, minmax? minmax?, minmax_by(&block : T -> U) forall U minmax_by, minmax_by?(&block : T -> U) forall U minmax_by?, minmax_of(&block : T -> U) forall U minmax_of, minmax_of?(&block : T -> U) forall U minmax_of?, none?(&block)
none? none?
, one?(&block) one?, partition(&block) partition, product(&block)
product(initial : Number, &block)
product
product(initial : Number) product
, reduce(&block)
reduce(memo, &block) reduce
, reject(&block : T -> ) reject, select(&block : T -> ) select, size size, skip(count : Int) skip, skip_while(&block) skip_while, sum(initial)
sum
sum(initial, &block)
sum(&block) sum
, take_while(&block) take_while, to_a to_a, to_h to_h, to_set to_set

Class Method Detail

def self.of(element : T)Source

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

def self.stopSource

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

Instance Method Detail

def chain(other : Iterator(U)) forall USource

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

def chunk(reuse = false, &block : T -> U) forall T, USource

Returns an Iterator that enumerates over the items, chunking them together based on the return value of the block.

Consecutive elements which return the same block value are chunked together.

For example, consecutive even numbers and odd numbers can be chunked as follows.

[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].chunk { |n|
  n.even?
}.each { |even, ary|
  p [even, ary]
}

# => [false, [3, 1]]
#    [true, [4]]
#    [false, [1, 5, 9]]
#    [true, [2, 6]]
#    [false, [5, 3, 5]]

The following key values have special meaning:

  • Enumerable::Chunk::Drop specifies that the elements should be dropped
  • Enumerable::Chunk::Alone specifies that the element should be chunked by itself

By default, a new array is created and yielded for each chunk when invoking #next.

  • If reuse is given, the array can be reused
  • If reuse is an Array, this array will be reused
  • If reuse is truthy, the method will create a new array and reuse it-

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion-

See also: Enumerable#chunks.

def compact_map(&func : T -> _)Source

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

def cons(n : Int, reuse = false)Source

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

By default, a new array is created and yielded for each consecutive when invoking #next.

  • If reuse is given, the array can be reused
  • If reuse is an Array, this array will be reused
  • If reuse is truthy, the method will create a new array and reuse it-

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion-

def cycle(n : Int)Source

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

def cycleSource

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

def eachSource

def each(&block) : NilSource

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"

def each_slice(n, reuse = false)Source

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

By default, a new array is created and yielded for each consecutive when invoking #next.

  • If reuse is given, the array can be reused
  • If reuse is an Array, this array will be reused
  • If reuse is truthy, the method will create a new array and reuse it-

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion-

def first(n : Int)Source

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

def flat_map(&func : T -> Array(U) | Iterator(U) | U) forall USource

Returns a new iterator with the concatenated results of running the block (which is expected to return arrays or iterators) once for every element in the collection.

iter = [1, 2, 3].each.flat_map { |x| [x, x] }

iter.next # => 1
iter.next # => 1
iter.next # => 2

iter = [1, 2, 3].each.flat_map { |x| [x, x].each }

iter.to_a # => [1, 1, 2, 2, 3, 3]

def flattenSource

Returns an iterator that flattens nested iterators and arrays into a single iterator whose type is the union of the simple types of all of the nested iterators and arrays (and their nested iterators and arrays, and so on).

iter = [(1..2).each, ('a'..'b').each].each.flatten
iter.next # => 1
iter.next # => 2
iter.next # => 'a'
iter.next # => 'b'
iter.next # => Iterator::Stop::INSTANCE

def in_groups_of(size : Int, filled_up_with = nil, reuse = false)Source

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

By default, a new array is created and yielded for each group.

  • If reuse is given, the array can be reused
  • If reuse is an Array, this array will be reused
  • If reuse is truthy, the method will create a new array and reuse it-

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion-

def map(&func : T -> U) forall USource

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

abstract def nextSource

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

def reject(&func : T -> U) forall USource

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

abstract def rewindSource

Rewinds the iterator to its original state.

def select(&func : T -> U) forall USource

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

def skip(n : Int)Source

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

def skip_while(&func : T -> U) forall USource

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

def slice(n : Int, reuse = false)Source

Alias of #each_slice.

def step(n : Int)Source

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

def stopSource

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

def take_while(&func : T -> U) forall USource

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

def tap(&block : T -> )Source

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

def uniqSource

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

def uniq(&func : T -> U) forall USource

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"]].each.uniq &.first
iter.next # => ["a", "a"]
iter.next # => ["b", "a"]
iter.next # => Iterator::Stop::INSTANCE

def with_index(offset : Int = 0)Source

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

def with_index(offset : Int = 0, &block)Source

Yields each element in this iterator together with its index.

def with_object(obj)Source

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

def zip(other : Iterator(U)) forall USource

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

© 2012–2017 Manas Technology Solutions.
Licensed under the Apache License, Version 2.0.
https://crystal-lang.org/api/0.22.0/Iterator.html

在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号

意见反馈
返回顶部