はじめに
Ruby は非常に多彩な文法やクラス、メソッドを持っており、1 つの処理を実装するのに複数の方法が存在することが多いです1。そのため、どの方法を採用すればよいか迷ったり、あるいはよりよい方法がないか気になったりする機会も多いです。そこで、そういうケースで役立つささやかな指針を提案します。それが
ある処理を実装するために複数の方法がある場合は、より特化した方を選ぼう
です。
具体例
ケース 1: 整数の配列の合計値を求める
配列などの Enumerable (列挙可能) なクラスを操作する場合、Array#each や Enumerable#map に続いて Enumerable#inject の使い方を学ぶ方も多いと思います。リファレンスには inject メソッドを用いて整数の配列の合計値を求めるコードが記載されています。
[2, 3, 4, 5].inject { |result, item| result + item } #=> 14
さらに inject の使い方に詳しくなれば、次のように書くかもしれません。
[2, 3, 4, 5].inject(:+) #=> 14
しかし、ここで立ち止まってやりたいのは、より特化した方法があるのではないかと模索することです。Ruby にはあたかもプログラマの思考を先回りしたかのように、おあつらえ向きのメソッドを用意してくれていることがよくあります2。
「合計を求めるのだから sum という名前のメソッドはないか」と Array のリファレンスで sum
をページ内検索してみましょう。やはりありました
[2, 3, 4, 5].sum #=> 14
ケース 2: 配列の先頭から n 個の要素を取得する
次の配列から先頭の要素を 2 つだけ抜き出したいです。
girls = ['ゆの', '宮子', '沙英', 'ヒロ']
先頭の要素を 1 つだけ抜き出すなら
girls[0] #=> "ゆの"
と書いたり、あるいは Array#first を使用して
girls.first #=> "ゆの"
と書けます。わざわざ先頭の要素を取得することに特化したメソッドが first という名前で用意されている。まさに Ruby らしいですね!
では先頭から複数の値を取得するためにはどうしたらよいでしょうか。
例えば Ruby と同じスクリプト言語の Python なら、スライスと呼ばれる機能を使用して次のように書けます。
# Python の世界
>>> girls = ['ゆの', '宮子', '沙英', 'ヒロ']
>>> girls[0:2] # スライスという機能を使って、0 番目 (先頭) から 2 つの要素を取得する。
['ゆの', '宮子']
しかし Ruby の配列にはスライスという機能はなく、同じ書き方をするとシンタックスエラーとなります。
girls[0:2] # SyntaxError
でも Ruby のことなので、必ず Python のスライスと同じような機能が存在するはずです!Ruby を信じるのです
今度は Array のリファレンスで slice
をページ内検索してみましょう。Array#slice というメソッドが見つけるはずです。
girls.slice(0, 2) #=> ["ゆの", "宮子"]
お見事!さすが Ruby
ちなみに Array#[] というメソッド (Ruby では配列の [] もメソッドなのです) では実現できないのでしょうか。リファレンスを確認すると、引数を 2 つ渡す、あるいは Range オブジェクトを渡すことで実現できることがわかります。
girls[0, 2] #=> ["ゆの", "宮子"]
girls[0..1] #=> ["ゆの", "宮子"]
喜ぶのはまだ早いです。さらに別の方法も!先ほど登場した Array#first のリファレンスをよく読んでみてください。引数を指定することで挙動が変わるのです。
girls.first(2) #=> ["ゆの", "宮子"]
なんと先頭から 2 つの要素を取得できました。Ruby は単にメソッドが多いだけではなく、メソッドの呼び方も豊富に用意されているところに多彩さを感じますね。
特化した方を選ぶメリット
特化した方を選ぶメリットですが、
- 引数やブロックを省略できることで、コードがシンプルになる (コード量が少し減る) 。
- メソッド名がより直感的で読みやすくなる。
と言えると思います。
例えば inject(:+)
より sum
の方が、slice(0, 2)
より first(2)
の方が引数の数が減り、なおかつ直感的に読めると思います。
また
- 他の方法よりパフォーマンスが向上していたり不具合が解消されていたりする。
というメリットが存在する場合もあります3。
まとめ
Ruby では 1 つの処理を実装するのに複数の方法が存在することが多いです。多くのメソッドが提供され、また同じメソッドでもさらに複数の機能を備えていることが多いです。そこで 慣れない処理を実装した際は、より特化した方法が存在しないかリファレンスを調べてみる ことをおすすめします。Ruby ならきっとドラえもんのひみつ道具のように、うってつけな方法を提供してくれることでしょう4
-
Ruby と同じスクリプト言語の Perl には "There's more than one way to do it (それをする方法はひとつじゃない)" という思想があり、Ruby と似ています。一方で、同じくスクリプト言語の Python には "There should be one-- and preferably only one --obvious way to do it (それをする明白な方法が一つあるはず。そして望ましいのは一つだけであること)" という正反対の思想があります。 ↩
-
かつて Ruby はよく 驚き最小の原則 (あるいは「Matz の驚き最小の原則」)とともに語られていたということですが、このように Ruby にはプログラマがほしいと思う機能が自然な形で存在するというのも理由のひとつなのでしょう。 ↩
-
Array#sum もこの例に該当します。
Ruby 2.4で追加されるArray#sumで配列の要素の総和が正確かつ高速になる ↩
-
もし Ruby にほしい機能が存在しない場合は、外部のライブラリである Active Support コア拡張機能 を調べてみるのも良いでしょう。Active Support とは Web フレームワークである Ruby on Rails の提供する一つの機能で、Array や Hash など Ruby の既存のクラスを拡張する形で便利なメソッドを提供しています。 ↩