PerlのARRAYをRubyのArrayっぽく扱うためのモジュールがすでに三個くらいあって大人気ですね.
こういったモジュールを使うと,
my @elems = map { $_ ** 2 } grep { $_ % 2 == 0 } (0..100); my $sum = 0; for my $n (@elems) { $sum += $n; }
とか書いていたコードが,
# List::Rubyishの場合 my $sum = List::Rubyish->new([0..100]) ->grep(sub{ $_ % 2 == 0}) ->map (sub{ $_ ** 2 }) ->reduce(sub { $_[0] + $_[1] });
みたいな感じのメソッドチェーンで書けるようになってオシャレですね.*1
しかし,こういうこと言ってると,
「List::Rubyish->new てなんなの.ちょーかっこわるいよ.リテラルでメソッド呼べるRuby最高!」
とか言ってPerlをDISりはじめるひとが現れるのは必至です.そこで, RubyLikeなメソッドをARRAYでautoboxして使える,autobox::UNIVERSAL::Listを書きました.
autobox::UNIVERSAL::List を使うと,RubyLikeなメソッドがARRAYにautoboxされます.すると,リテラルから直接メソッドを呼ぶことが可能になるので,上の例が
use autobox::UNIVERSAL::List; my $sum = [0..100]->grep(sub{ $_ % 2 == 0}) ->map (sub{ $_ ** 2 }) ->reduce(sub { $_[0] + $_[1] });
と書けるようになります.これでRubyistも何もいえないですね!
デフォルトでは,List::Rubyshが裏で使われるのですが,
use autobox::UNIVERSAL::List impl_class => "List::RubyLike"; my $sum = [0..100]->grep(sub{ $_ % 2 == 0}) ->map (sub{ $_ ** 2 }) ->reduce(sub { $_[0] + $_[1] });
とかすると,List::RubyLikeを使うこともできるので,List::Rubyshがふるまいが気に入らなくても安心です.
impl_classには,ARRAYから生成できてかつ,結果をARRAYを返すことのできるクラスならば,なんでも指定できます.たぶん.なので,RubyLikeなArrayのメソッド実装以外のものを組み合わせることが簡単にできちゃったりするはずです.あんまりためしてないので,不安です.
ARRAY生成のためのコンストラクタや結果をARRAYで返す部分は, useするときに指定することもできます.
use autobox::UNIVERSAL::List impl_class => "List::Enumerator::Array", new => sub { List::Enumerator::Array->new(array => shift) }, to_a => sub { shift->to_a }; # my $sum = [0..100]->grep(sub{ $_ % 2 == 0}) # ->map (sub{ $_ ** 2 }) # ->reduce(sub { $_[0] + $_[1] }); # ただし,List::Enumerator::Array は 多くのメソッドをList::Enumerator::Roleから # 継承してるのでうまく動かない
制限としては,impl_classが何か別のクラスを継承している場合はうまくいかないので,List::Enumerator::Arrayを使うことはできません.ここはどうにかしたいのですが,クラスの持っているメソッド名を継承元もたどって全部とれたらなんとかできそうです.
さらに,overloadされた演算子もうまく動きません.リストリテラルの演算子のoverloadとか怖すぎます.というかできるのかもよく知りません.
というわけで,use autobox::UNIVERSAL::Listの紹介でした.正直,わざわざautoboxにする必要がないので,実用性はあんまりな気がしますが,おしゃれなスクリプトをクールに決めたい人はおもむろにuseするといいかもしれません.
*1:例がいまいちすぎるなー.おしゃれな例ないかなー