Ruby 4.1 リファレンスマニュアル

singleton method Enumerator.produce

produce(initial = nil, size: nil) { |prev| ... } -> EnumeratorRuby 2.7.0 から[permalink][rdoc][edit]

与えられたブロックを呼び出し続ける、停止しない Enumerator を返します。ブロックの戻り値が、次にブロックを呼び出す時に引数として渡されます。 initial 引数が渡された場合、最初にブロックを呼び出す時にそれがブロック呼び出しの引数として渡されます。initial が渡されなかった場合は nil が渡されます。

ブロックが例外 StopIterationを投げた場合、繰り返しが終了します。

[PARAM] initial:
ブロックに最初に渡される値です。任意のオブジェクトを渡せます。
[PARAM] size:
生成する Enumerator の要素数(Enumerator#size で取得できる値)を指定します。整数、Float::INFINITYProc などの呼び出し可能なオブジェクト、または要素数が不明であることを表す nil を指定できます。省略した場合は Float::INFINITY になります。
# 1, 2, 3, 4, ... と続く Enumerator
Enumerator.produce(1, &:succ)

# next を呼ぶたびランダムな数値を返す Enumerator
Enumerator.produce { rand(10) }

# ツリー構造の祖先ノードを列挙する Enumerator
ancestors = Enumerator.produce(node) { |prev| node = prev.parent or raise StopIteration }
enclosing_section = ancestors.find { |n| n.type == :section }

このメソッドは Enumerable の各メソッドと組み合わせて使うことで、 while や until ループのような処理を実装できます。例えば Enumerable#detect, Enumerable#slice_after, Enumerable#take_while などと合わせて使えるでしょう。

Enumerable のメソッドと組み合わせる例
# 次の火曜日を返す例
require "date"
Enumerator.produce(Date.today, &:succ).detect(&:tuesday?)

# シンプルなレキサーの例
require "strscan"
scanner = StringScanner.new("7+38/6")
PATTERN = %r{\d+|[-/+*]}
p Enumerator.produce { scanner.scan(PATTERN) }.slice_after { scanner.eos? }.first
# => ["7", "+", "38", "/", "6"]

生成する Enumerator の要素数を size: で指定できます。

size: を指定する例
# 整数を渡すと Enumerator#size がその値を返す
enum = Enumerator.produce(1, size: 5, &:succ)
p enum.size     # => 5

# Float::INFINITY(size: を省略した場合のデフォルトと同じ)
inf = Enumerator.produce(1, size: Float::INFINITY, &:succ)
p inf.size      # => Infinity

# 呼び出し可能オブジェクト(size を参照するたびに評価される)
callable = Enumerator.produce(1, size: -> { 42 }, &:succ)
p callable.size # => 42

# nil は要素数が不明であることを表す
unknown = Enumerator.produce(1, size: nil, &:succ)
p unknown.size  # => nil