3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Rubyのyieldとtapとthen

Last updated at Posted at 2021-03-28

はじめに

こないだrubyのthen(yield_selfのエイリアス)を使ったら、
thenが良くわからない」と質問がありました。
自分も昔、躓いた気がするので
rubyのyieldtapthenをまとめておきます。

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があったら、
**「あ、このメソッドに渡されてるブロックを実行してるんだな」**と思ってください。
:warning: 記事が理解できたら、公式ドキュメントとか他のものも見てください。

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)とは?

実はここまで理解してれば超簡単です。
:warning: 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が分からなくて詰む。

ということが、昔の自分はあったのでまとめてみました。
それぞれの良さは、別の記事を見てみてください。
個人的には、どれも多用すべきじゃないと思う...

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?