概要
Ruby幼稚園生の自分にとって、module Enumerableに書いてある、
インクルードするクラスには each が定義されていなければなりません。
が、まぁよくわからなかったので、もう少しだけ理解を深めたいと思い書いてみた。
そもそもEnumerableって何なのよ?
繰り返し処理を行うクラスに、includeされているモジュール。
概要で書いた通り、インクルード先のクラスにはeachが定義されている必要がある。
そのため、Enumerableをインクルードしている「Array」「Hash」「Range」クラスあたりでは、ちゃんとインスタンスメソッドとしてeachメソッドが存在する。
自分が定義したオリジナルのクラスでEnumalebleのメソッドを使いたかったら、そのオリジナルクラスでeachメソッドを定義しておかなければいけない、ということになる。
まだ中々イメージが湧きづらいので、軽く実験をしながら、オリジナルのクラスでもEnumerableメソッドを使えるようにしてみる。
① eachメソッドを定義しなくてもincludeできるのか?
結論:できる。インスタンスの作成もできる。
class Foo include Enumerable def hello puts 'hello' end end foo = Foo.new foo.hello #=>hello
もちろんこの段階でEnumerableのメソッドを使おうとすると、エラーが出てしまうので注意。
foo.map(&:upcase) #=>:in `map': undefined method `each' for #<Foo:0x00007f6950995488> (NoMethodError)
② Fooクラスにeachメソッドを定義してみる
class Foo include Enumerable def initialize(array) @array = array end def each @array.each{|i| yield i } end end foo = Foo.new([1, 2, 3, 4, 5]) foo.map{|i| i * 2} #=> [2, 4, 6, 8, 10]
初期化の際に作成したインスタンス変数@array
は、Arrayクラスなので、当然
@array.each{|i| yield i }
のようにeachメソッドを扱うことができる。
③ eachメソッドの定義を変えてみる
class Foo include Enumerable def initialize(array) @array = array end def hello puts 'hello' end def each @array.each{|i| yield i.to_s(2)} end end foo = Foo.new([1, 2, 3, 4, 5]) foo.sum('#') #=> "#11011100101"
今回は、eachメソッドの定義の段階で、to_sメソッドを呼び出し、全て2進数にしたあとに、yieldメソッドでブロックに値を渡すようにした。
元々のeachメソッドの定義や、yield・Procオブジェクトをもっとうまく活用できれば、更に柔軟な設計が出来そうだ。