Ruby

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

More than 1 year has 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