Enumerableモジュール

どうしてArrayには条件にマッチしないものを返すrejectとかdelete_ifがあるのに、その逆はないんだろうかと不思議に思っていたけど、それはマニュアルの見方が分かってなかっただけのようだ。というか、クラスやモジュールの相関関係を理解してなかったということらしい。Arrayのメソッド一覧の下の方に「継承したメソッド」というのがずらずらと列挙してあったのだけど、その意味をいまいち理解していなかった。

ArrayはEnumerableモジュールというのをインクルード(ニアリーイコール継承?)していて、このモジュールにはany?とかfindみたいなメソッドが色々あったようだ。Enumerableは列挙可能だとかカウント可能な集合を指すようで、ArrayのほかにもRangeやHash、Stringなんかもこのモジュールをインクルードしているらしい(って、StringがEnumerableってなんだ?)。

これがMix-inを使ったクラス設計の意味なのかしら。なんと、eachを定義してさえあれば、自作クラスにもEnumerableモジュールをインクルードできて、いきなり便利そうな大量のメソッドが使えちゃうらしい。すごいやんか。すごいし、すっきりしているように思うけど、継承みたいな概念ってほかの言語ではどうなってるのか知らないからMix-inがどのぐらいすごいのか良く分からない。Rubyには多重継承がない代わりにMix-inがある、とどこかで読んだ気がする。

ともあれ、以下の表現は何もArrayに変換してからブロックを適用する必要はない。

(1..100).to_a.reject!{|i| (i % 2) == 0}

find_allを使って、いきなり範囲オブジェクトに対してブロックを渡せる。以下のように書き直せるようだ。

irb(main):005:0> (1..100).find_all{|i| (i % 2) != 0}
=> [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 
35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 
69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]

rejectとfind_allは、ちょうど正反対の役割で、ブロックの中身の真偽を反転させればどちらか一方があれば事足りる。だからrejectしかないのかと思った。でも、やっぱりロジックを表現するときの自然さや、後からコードを眺めたときの理解のしやすさということを考えると、reject(a)とfind_all(!a)はだいぶ違う。

さて、まったく同様に、以下のコードもより端的に次のように書き換えられる。

slist.each {|file|
      if (file[2] == num)   # event_id
        @event.push file
      end
    }
@event = slist.find_all {|file| file[2] == num}

これはすごい。