意識して使ってないけど、配列のeachメソッドやループのtimesなどがこれに該当する。
Enumerableオブジェクト
組み込みオブジェクトのArrayやHashがincludeしているモジュール。Enumerableをincludeして、eachメソッドを定義してあげれば独自モジュールでもEnumerableが提供する様々なメソッドを利用出来る。
Arrayを使った場合のコード
array = [1, 2, 3]
# eachでループ
array.each {|x|
puts x
}
# 添字付きでループ
array.each_with_index {|x, i|
puts "#{i} = #{x}"
}
出力結果はこんな感じ1 2 3 0 = 1 1 = 2 2 = 3
独自クラスの場合
class Hoge
include Enumerable
def initialize(num)
@num = num
end
def each
@num.each {|num|
# prefixにhoge_を付加する。
yield "hoge_#{num}"
}
end
end
出力結果はこんな感じEnumerableで定義されてるgrepもちゃんと使えてるのが分かる。
each start... hoge_1 hoge_2 hoge_3 hoge_4 grep start... hoge_2 hoge_3
ruby始めた頃は、each_with_indexなんて知らなくて、こんなループをかいてた・・・。
まぁ、動くから間違ってはないけど直感的ではないよね。一瞬なにがしたいんだ?って思ってしまう。
array.count.times {|i|
puts "array[#{i}] = [#{array[i]}]"
}
Enumeratorオブエクト
each以外のメソッドでもEnumerableを使うためのラッパークラスEnumeratorはイミュータブルなので、生成したあとの状態変更はできない。
なので、配列でパラメータ渡しをするのではなくEnumeratorを指定したほうが良い。
# eachメソッドを定義しているクラスのto_enumを呼び出すと、Enumeratorを生成できる。
enum = array.to_enum
enum.each {|x|
puts x
}
# enumメソッドを定義していないクラスの場合
class Hoge
def initialize(num)
@num = num
end
def hoge
@num.each {|num|
yield "hoge_#{num}"
}
end
end
hoge = Hoge.new([1, 2, 3, 4])
# enum_forメソッドに、eachと同義のhogeメソッドを指定して Enumeratorを生成する。
enum = hoge.enum_for(:hoge)
puts "each start..."
enum.each {|x|
puts x
}
puts "grep start..."
enum.grep(/^.*[23]$/) {|x|
puts x
}
実行結果はこんな感じhogeメソッドしか持っていなくても、Enumerableのメソッドを利用できている。
each start... hoge_1 hoge_2 hoge_3 hoge_4 grep start... hoge_2 hoge_3