Rubyのイテレータなメソッドの中には「ブロックを指定しないとEnumeratorを返す」メソッドがいっぱいありますが、そういうメソッドの作り方がRuby歴数ヶ月の自分にはわからなかったので調べました。
- ブロックが指定されたかどうかはblock_given?でわかる
- Enumeratorを返すんだからEnumerator.newとかするのかな?
- Enumeratorの説明を見てみる
- Enumeratorを生成するにはEnumerator.new、Object#to_enum、Object#enum_forの3つの方法があるらしい
- おもむろに~/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