はじめに
こないだrubyのthen
(yield_self
のエイリアス)を使ったら、
「then
が良くわからない」と質問がありました。
自分も昔、躓いた気がするので
rubyのyield
とtap
とthen
をまとめておきます。
yieldとは?
簡単に言うと、「メソッドに渡されたブロックを実行できる」ものです。
(ブロックは do ~ end
の処理の塊です)
yieldのサンプル
簡単なサンプルを作ってみました。
# 適当なメソッドを定義。中でyieldを実行
def execute_yield
yield
end
# ブロックをメソッドに渡す
execute_yield do
puts "Hello World"
end
irbなどで実行すると、以下のような出力がされます。
Hello World
=> nil
これはexecute_yield
メソッドに渡されたブロック(do ~ endの塊)が、
yield
によって呼び出されたためです。
なので、メソッドの中でyieldがあったら、
**「あ、このメソッドに渡されてるブロックを実行してるんだな」**と思ってください。
記事が理解できたら、公式ドキュメントとか他のものも見てください。
block_given?
ちなみに、メソッドでyieldを使ってるのに、メソッドにブロックが渡されていないと、怒られます
# さっき作ったメソッドをブロック(do~endの塊)なしで呼び出してみる
execute_yield
LocalJumpError (no block given (yield))
時々、「ブロックが渡されているか」で分岐させたいときがあります。
そんなときは、block_given?
メソッドが使えます。
さっき作ったメソッドを、以下のように書き換えてみましょう
def execute_yield
if block_given?
yield
else
puts "No block!!"
end
end
それぞれ、以下のような出力がされるはずです。
# ブロックあり
execute_yield do
puts "Hello World"
end
# ブロックがあるときは、ブロックのputs "Hello World"が実行される
Hello World
=> nil
# ブロックなし
execute_yield
# No block!!が表示される
No block!!
=> nil
tap
公式ドキュメント
とりあえず、まずは公式ドキュメントです。
tapとは?
公式ドキュメントより抜粋
self を引数としてブロックを評価し、self を返します。
良く分からなくなってきましたね!
実装のイメージはこんな感じのハズです。
def tap(&block) # selfを引数
yield(self) # ブロックを評価(実行)
self # selfを返す
end
(他の記事や、Cで書かれたRubyを、Rubyで再実装している、rubiniusのコード的にも合っているはずです)
日本語はややこしいですが、してることは単純で、
「yield(渡されたブロックを実行)して、self(レシーバ)を返す」です。
サンプル
tapの小さめのサンプルを作ってみました。
"hoge".tap do |string|
# 小文字を大文字に変換
p string.upcase
end
"HOGE" # pで出力される "HOGE"
=> "hoge" # メソッドの返り値
tap
は中でyield
を呼び出します。
yield
なので、ブロック(do~endの間)で渡しているstring.upcase
を実行。「"HOGE"」が出力されます。
ただし、tapの返り値はselfなので、メソッドで返されるのは
"hoge".tap
の「"hoge"」です。
使いみち
メソッドチェーンするときのデバッグなどに使えます。
公式ドキュメントのサンプルをそのまま流すと、途中でputsされているのが分かって
分かりやすいですね。(公式ドキュメントが正義)
# tapで長くなってますが、していることは、
# 配列にして、偶数だけ取って、二乗してるだけです。
(1..10).tap {|x| puts "original: #{x}" }.
to_a.tap {|x| puts "array: #{x}" }.
select {|x| x.even? }.tap {|x| puts "evens: #{x}" }.
map {|x| x*x }.tap {|x| puts "squares: #{x}" }
# 出力結果と返り値
original: 1..10
array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens: [2, 4, 6, 8, 10]
squares: [4, 16, 36, 64, 100]
=> [4, 16, 36, 64, 100]
then(yield_self)とは?
実はここまで理解してれば超簡単です。
ruby2.5以降。エイリアスのthen
は2.6以降でしか使えません。
以下を参照
tap
はyieldして、selfを返していましたが、then
はyieldの結果を返します。
サンプル
同じようなサンプルを使います。
"hoge".then do |string|
# 小文字を大文字に変換
p string.upcase
end
"HOGE" # pで出力される "HOGE"
=> "HOGE" # メソッドの返り値
返り値がブロックの実行結果("HOGE")になっています。
凄い手抜きぽくなっていますが、
tap
との違いは、
- レシーバを返すか?
- ブロックの実行結果を返すか?
です。
最後に
「何番煎じなんだ?」という記事ですが、
「yieldとは?」「tapとthenの比較」とかあるのに、まとめたものはイマイチない(気がした)
業務では、唐突に出てきたthen
を調べるが、そもそもyield
が分からなくて詰む。
ということが、昔の自分はあったのでまとめてみました。
それぞれの良さは、別の記事を見てみてください。
個人的には、どれも多用すべきじゃないと思う...