まえがき
Object#yield_self
、使ってますか?
Object#yield_self
はRuby2.5から追加された強力なメソッドです。
今回はその使い方を、似たメソッドである Object#tap
と比較しながら説明します。
中身の実装
Object#yield_self
class Object
def yield_self
yield self
end
end
Object#tap
ネイティブで実装されていてRubyコードじゃなくて辛いので、
RubyのRuby実装であるRubiniusからソースを持ってきます。
module Kernel
def tap
yield self
self
end
end
#yield_selfと#tapの相違点
yield
自体の挙動がちょっとむずかしいので難解かもしませんが、
どちらもレシーバを引数のブロックの引数にとって実行する点は同じです。
どちらもレシーバをブロックのパラメーターにとって実行する点は同じです。
※用語が不正確だというご指摘いただきましたので修正
違うのは返り値で
yield_self
だと実行結果(つまり受け取ったブロックの返り値)が返って、
tap
はブロックを実行した後のレシーバが返ります。
つまりどういうことだってばよ
ブロックの中で String#downcase!
を使う例で比較します。
String#downcase!
はレシーバの文字列を小文字にして返す破壊的メソッドですが、
変更があった場合には小文字化した文字列が返り値になり、変更なしの場合はnilが返ります。
'Hoge'.downcase! # => "hoge"
'hoge'.downcase! # => nil
ただし破壊的とはいえ、そこまで暴君ではないので元の文字列までnilにするわけではありません
str = 'hoge'
str.downcase! # => nil
str # => 'hoge'
さてこれをyield_selfとtapで使ってみましょう。
'hoge'.yield_self { |str| str.downcase! } # => nil
'hoge'.tap { |str| str.downcase! } # => 'hoge'
yield_selfはブロックの中の実行結果が返るので、 'hoge'.downcase!
の返り値である nil
が返り、
tapは実行後のレシーバが返るのでそのまま 'hoge'
が返りました。
で、yield_selfの何がいいのか
たとえばこんなコードをリファクタしてみる。
before = 'hello world'
str = 'L'.downcase
p str # => 'l'
str = str * 2
p str # => 'll'
after = before.gsub(str, 'X')
p after # => "heXo world"
( self =
とかはナシで)tapを使うとせいぜいこんなものでしょう↓
before = 'hello world'
str = 'L'.downcase.tap { |tmp| p tmp } # => 'l'
str = (str * 2).tap { |tmp| p tmp } # => 'll'
after = before.gsub(str, 'X').tap { |tmp| p tmp } # => "heXo world"
yield_selfを使うとこんな感じ
before = 'hello world'
after = 'L'.downcase
.yield_self { |str| p str; str * 2 } # => 'l'
.yield_self { |str| p str; before.gsub(str, 'X') } # => 'll'
.tap { |str| p str } # => "heXo world"
いちいち変数にいれなくても次のメソッドで使えるので幾分かスマートになりました。
元のコードと比べて、変数(引数)のスコープが限定的になるのでコードを読むときに「この変数なんだっけ?」という状態になりづらくてとても便利です。
おわり
tapでメソッドチェーンを作るのもいいですが、時にはyield_selfのことも思い出してあげてください。
ちなみにtap
と break
で同じことできます。
ruby2.6からは then
でかけます