Posted at

ブロックを与えない場合にEnumeratorを返すメソッドを作る

More than 5 years have passed since last update.

Rubyのイテレータなメソッドの中には「ブロックを指定しないとEnumeratorを返す」メソッドがいっぱいありますが、そういうメソッドの作り方がRuby歴数ヶ月の自分にはわからなかったので調べました。


  1. ブロックが指定されたかどうかはblock_given?でわかる

  2. Enumeratorを返すんだからEnumerator.newとかするのかな?

  3. Enumeratorの説明を見てみる

  4. Enumeratorを生成するにはEnumerator.new、Object#to_enum、Object#enum_forの3つの方法があるらしい

  5. おもむろに~/lib/ruby/の下でgrepしてみたらそれっぽいコードが引っかかる

というわけで、本体同梱クラスをお手本にして、こうすればよいということがわかりました。

class Foo

def initialize(str)
@str = str
end

def each_word(minlength = 1)
# ↓これ
return enum_for(__method__, minlength) unless block_given?

@str.split(/\W+/).each do |word|
yield(word) if word.length >= minlength
end
end
end

ブロックが指定されなかったときは、自身のメソッド名を引数にしてenum_forでEnumeratorを返してやればよいわけですね。

※Kernel#__method__は現在のメソッド名を返します。

こんな感じに使えるようになりました。

foo = Foo.new('Lorem ipsum dolor sit amet, consectetur adipisicing elit')

foo.each_word(5) do |v|
puts v
end
puts

words = foo.each_word(7)
p words
words.with_index do |v, i|
puts "#{i}: #{v}"
end

Lorem

ipsum
dolor
consectetur
adipisicing

#<Enumerator: #<Foo:0xc42618 @str="Lorem ipsum dolor sit amet, consectetur adipisicing elit">:each_word(7)>
0: consectetur
1: adipisicing