Skip to content
class method

Enumerator.produce

[T] () { (T? prev) -> T } -> ::Enumerator[T, bot]
[T] (T initial) { (T prev) -> T } -> ::Enumerator[T, bot]
Enumerator.produce(initial = nil, size: nil) { |prev| block } → enumerator

Creates an infinite enumerator from any block, just called over and over. The result of the previous iteration is passed to the next one. If initial is provided, it is passed to the first iteration, and becomes the first element of the enumerator; if it is not provided, the first iteration receives nil, and its result becomes the first element of the iterator.

Raising StopIteration from the block stops an iteration.

Example
Enumerator.produce(1, &:succ)   # => enumerator of 1, 2, 3, 4, ....

Enumerator.produce { rand(10) } # => infinite random number sequence

ancestors = Enumerator.produce(node) { |prev| node = prev.parent or raise StopIteration }
enclosing_section = ancestors.find { |n| n.type == :section }

Using ::produce together with Enumerable methods like Enumerable#detect, Enumerable#slice_after, Enumerable#take_while can provide Enumerator-based alternatives for while and until cycles:

Example
# Find next Tuesday
require "date"
Enumerator.produce(Date.today, &:succ).detect(&:tuesday?)

# Simple lexer:
require "strscan"
scanner = StringScanner.new("7+38/6")
PATTERN = %r{\d+|[-/+*]}
Enumerator.produce { scanner.scan(PATTERN) }.slice_after { scanner.eos? }.first
# => ["7", "+", "38", "/", "6"]

The optional size keyword argument specifies the size of the enumerator, which can be retrieved by Enumerator#size. It can be an integer, Float::INFINITY, a callable object (such as a lambda), or nil to indicate unknown size. When not specified, the size defaults to Float::INFINITY.

Example
# Infinite enumerator
enum = Enumerator.produce(1, size: Float::INFINITY, &:succ)
enum.size  # => Float::INFINITY

# Finite enumerator with known/computable size
abs_dir = File.expand_path("./baz") # => "/foo/bar/baz"
traverser = Enumerator.produce(abs_dir, size: -> { abs_dir.count("/") + 1 }) {
  raise StopIteration if it == "/"
  File.dirname(it)
}
traverser.size  # => 4

# Finite enumerator with unknown size
calendar = Enumerator.produce(Date.today, size: nil) {
  it.monday? ? raise(StopIteration) : it + 1
}
calendar.size  # => nil

Ruby version changes

  1. 4.0
    • Enumerator.produce now accepts an optional size keyword argument to specify the size of the enumerator. It can be an integer, Float::INFINITY, a callable object (such as a lambda), or nil to indicate unknown size. When not specified, the size defaults to Float::INFINITY.

      Example
      # Infinite enumerator
      enum = Enumerator.produce(1, size: Float::INFINITY, &:succ)
      enum.size  # => Float::INFINITY
      
      # Finite enumerator with known/computable size
      abs_dir = File.expand_path("./baz") # => "/foo/bar/baz"
      traverser = Enumerator.produce(abs_dir, size: -> { abs_dir.count("/") + 1 }) {
        raise StopIteration if it == "/"
        File.dirname(it)
      }
      traverser.size  # => 4
      
See also .new .product #+ also in 4.1 · 3.4 · 3.3 · 3.2 · 3.1 · 3.0