Ruby のメソッドチェインって便利で楽しい


Ruby のメソッド呼び出しを連結させるメソッドチェインは日本語のように次から次へと連ねていくことができます.

メソッドチェインは一行に複数のメソッドを連ねていくことも:

puts 'method chains'.prepend("Ruby's ").concat(' are convenient and enjoyable.')

# => Ruby's method chains are convenient and enjoyable.

次の行にまたいで連ねていくこともできます (Ruby 1.9 より):

puts 'method chains'
  .prepend("Ruby's ")
  .concat(' are convenient and enjoyable.')

# => Ruby's method chains are convenient and enjoyable.

なので複雑なテキスト処理なんかもこのメソッドチェインを利用すると, 一つ一つのメソッドに分けてどのように処理していけばいいのかを考えられるので, Ruby のわかりやすいメソッド名と相まってとても読みやすいコードを書くことができます.

例えば次のようなテキスト処理もお茶の子さいさいです:

processed_string = "Ruby's method chains are convenient and enjoyable."
  .sub('are ', '')
  .prepend("Aren't ")
  .delete_suffix('.')
  .concat('?')
  .sub(/\b(m.+s)\b/, '"\1"')

puts processed_string
# => Aren't Ruby's "method chains" convenient and enjoyable?

メソッドチェインを強調させるために少し冗長になってしまいましたが, なかなか素敵だと思いませんでしょうか.

これらのメソッドは全てオブジェクトのコピーを返すので, このように連結させることができます.

オブジェクトを返すメソッドだったらメソッドチェインとして連ねていくことは可能ですが, メソッドが呼び出されるレシーバのオブジェクトのコピーを返すようなメソッドだとメソッドチェインとの相性が特に良くなります.

なので今回はそのようなメソッドだけ使っていこうと思います.

ところでメソッドを次から次へと付け足していくのって, おもちゃのレゴブロックで何かを組み立てていく感覚に似ているので純粋に楽しいです.

こういう楽しいという気持ちにさせてくれることってプログラミングをする上では結構重要なことだと感じさせてくれます.

これも Ruby の作者, まつもとゆきひろさんがこのように設計してくれたおかげですね.

次は Array でメソッドを連ねてみたいと思います:

processed_array = %w[Ruby's method chains are convenient and enjoyable.]
  .reject { |item| item == 'are' }
  .unshift("Aren't")
  .slice(0..-2)
  .push('enjoyable?')
  .reject { |item| %w[method chains].include? item }
  .insert(2, *%w["method chains"])
  .join(' ')

puts processed_array
# => Aren't Ruby's "method chains" convenient and enjoyable?

ややカオスな印象を受けるかもしれませんが, 一つ一つのメソッドでやっていることはとてもシンプルです.

配列式 [] の代わりにパーセント記法 %w[] を使っていたり, reject メソッドでブロックを呼び出していたり, スプラッタ演算子 * を使って %w["method chains"]insert メソッドの第二, 第三引数に変換させているくらいです.

このように一つ一つはシンブルなことでも組み合わせるとそれなりに複雑なことができてしまいます.

うん, やっぱり楽しいw

ということで, お次は Hash でメソッドを同じように連ねていきたいと思います:

processed_hash = { 1 => "Ruby's",
                   2 => 'method',
                   3 => 'chains',
                   4 => 'are',
                   5 => 'convenient',
                   6 => 'and',
                   7 => 'enjoyable.' }
  .reject { |k, v| k == 4 }
  .merge({ 0 => "Aren't" })
  .merge({ 7 => 'enjoyable?' })
  .merge({ 2 => '"method',
           3 => 'chains"' })
  .to_a
  .sort
  .map { |arr| arr[1] }
  .join(' ')

puts processed_hash
# => Aren't Ruby's "method chains" convenient and enjoyable?

ちょっと無理矢理感があって申し訳ないです.

理想的には全て Hash のメソッドで最終的に今までの文字列に変換させたかったのですが, 今の僕の実力では途中から Array に変換させざるをえませんでした.

一体何の縛りをやっているのでしょうかと言われてしまいそうですが, 本当に僕は何をやっているのでしょうね.

でも今までの流れをみていただければ, この一見意味不明とも思える縛りも自然な成り行きという風にとらえていただけるのではないかなと淡い期待をしております.

と少し遊んでしまいましたが, 要するにメソッドを連ね連ねて元の値から少しずつ変化させていくというのは, 強力で便利で実用的で楽しいということなのです.

まとめ

ということで今回は Ruby のメソッドチェインについて紹介させていただきました.

少し遊んでしまった感はありますが, Ruby のメソッドチェインの素晴らしさを少しでもお伝えすることができましたら幸いです.

ひいてはこのようなところに Ruby の魅力があるんだなということを感じていただければこれまた幸いです.

ただ今回, 例とさせていただいたコードはあくまでメソッドチェインの使い方をお見せするという目的で書かせていただきましたので, 実際のケースで使われるときには僕のような無駄に遊び心のある冗長なコードはお控えいただければと思います.

Ruby のメソッドチェインは Ruby のプログラミングを快適にしてくれる素晴らしいものですので Rubyist な方々はぜひ適材適所使っていきたいですね.