#はじめに
未経験エンジニアやエンジニア1年生のあなたは、「ブロック・yield・call・Proc・ラムダ式」を人に説明することができますか?
この辺りは避けて通ってきた人も多い知識だと思うので、不安な人はこの機会に全部まとめて学んじゃいましょう!!!
では超わかりやすい解説スタートです!
そもそもブロックとは
【記法】
do ... end, { } のどちらか。この記号と記号に囲まれた中身を含めてブロックという。
【ブロックの定義】
メソッド呼び出しの際に引数と一緒に渡すことのできる処理のかたまり | たのしいRuby P199
メソッド呼び出しの際というのがポイント。
てことで例えば、
def greeting
puts 'おはよー!'
end
# メソッドの呼び出し
greeting do
# 特に意味はないけど、メソッド呼び出し時に
# こうやってdo ... end で囲めばブロックを渡したことになる。
end
この場合の出力は、もちろん 'おはよー!' です。
ブロックの活用法
さっきのgreetingメソッドの例は全く意味のない使い方ですね。
まあ、ブロックはメソッドに渡す事ができるって理解できたらOKです!
さあ、ここからはブロックの有効的な活用法をご紹介しますが、みなさんがdo ... end を見て最初に思い浮かぶのは、eachじゃないでしょうか?
(私だけだったらすみません...)
未経験の方は、
「ブロックってメソッドの後につけるんだよね。そもそもeachってメソッドなの?」
こんな疑問の声が聞こえた気がしたので、一応説明しておくと、eachもメソッドです!
・「メソッドはクラスに定義されているもの」
・「(インスタンス)メソッドはオブジェクトに対して使用する」
っていう基本ルールはOKですかね?
実はeachメソッドは、Enumerable クラスに定義されているメソッドなのです!
Enumerable は、日本語で「列挙可能な」的な意味です。データが列挙されているデータ型をイメージしてみてください。
ご名答!
配列・ハッシュ・集合などが全てEnumerableクラスに属しています。(すぐ思い浮かんだ未経験の方はすごい!!!)
話がそれましたが、だからeachメソッドは、
[1, 2, 3].each
って感じで配列の後ろに付けられるんですね!
メソッドの後ろにブロックをつけることができるので、
[1, 2, 3].each do |ele|
end
ってできるわけですね。
ここで、eachとブロックの役割について解説しておきます。
each ... 配列の要素を一つずつブロックの中に渡す。
ブロック ... 要素を受け取った後の処理内容。
ブロックの概要は伝わりましたでしょうか!
#yield
次はyieldです!
yieldの役割は、**「メソッドの呼び出しをブロック付きで行った際、メソッドの中のyieldでブロックの処理内容を実行」**することです。
こちらの理解はなかなか難しいと思うので、またgreetingを使ったコードで説明します!
下のコードを見てください!
def greeting
puts 'おはよー!'
yield # greeting メソッドをブロック付きで呼び出した時にブロックの処理内容がここに入ります。
end
# greetingメソッドをブロック付きで呼び出す
greeting do
puts 'こんばんはー!!'
end
'おはよー!'
'こんばんはー!'
yieldの位置にブロック内の処理であるputs 'こんばんはー!!'が入ることで、出力に示したような結果になりました。
※ メソッド内にyieldを書いている時に、ブロックなしでメソッドを呼び出すとエラーが発生します。お気をつけください。
def greeting
puts 'おはよー!'
yield # greeting メソッドをブロック付きで呼び出した時にブロックの処理内容がここに入ります。
end
# greetingメソッドをブロックなしで呼び出す
greeting
LocalJumpError: no block given (yield)
こういった場合は、block_given?メソッドを使います。このメソッドはブロックが渡されているときにtrueを返します。
def greeting
puts 'おはよー!'
if block_given? # ブロックが渡された場合にyieldを実行する
yield
end
end
# greetingメソッドをブロックなしで呼び出す
greeting
'おはよー!'
ブロックを引数で渡す。Callでブロックを実行する。
ブロックを引数に渡したい場合は、以下の記法を使います。
def method(&ブロック)
# ブロックを実行する
ブロック.call
end
ブロックを渡してブロックの処理内容をcallで実行するってことですね!
これも例を見せておきます。
def greeting(&b)
puts 'おはよー!'
text = b.call('こんばんはー') # ブロックの処理内容を実行
puts text
end
# ブロックを引数に渡して実行
greeting do |text|
puts "#{text}!!"
end
おはよー!
こんばんはー!!
callでブロックを呼び出す際は引数も取ることができます。
yieldとcallの違いは何?
yieldもcallもブロックを呼び出すという点では同じです。
では何が違うのかというと、**「callは引数から明示的にブロックを渡す事ができる」**ということですね!
はて?って人はもう一度yieldとcallの説明を見直していただければ納得できると思います。
yieldではメソッドの定義の際、引数としてブロックを受け取りません。メソッド呼び出し時にブロックを渡していれば、yieldでブロック内の処理内容を実行します。
対してcallは、メソッドの定義の際、引数として「&ブロック」という表記でブロックを受け取ります。ブロックを渡している場合は、ブロック.callでブロック内の処理内容を実行します。
どちらも**「ブロック内の処理内容を実行する」**ということを理解しておいてください!
Proc
Rubyには、Procクラスというクラスが存在します。
Procクラスは、今までさんざん説明してきたブロックをオブジェクト化するためのクラスです。
Procクラスのオブジェクトを作成する時に、引数でブロックを渡します。
greeting = Proc.new { 'おはよー!' }
このままgreetingを呼び出してもブロックオブジェクトを保存しているメモリアドレスを返すだけでブロック内の処理内容は実行されません。
(メモリアドレスの話も今度詳しく解説する予定なので、気になる方は僕のTwitterで最新情報をチェックしてみてください!)
ブロックの処理内容を実行するメソッド覚えてます?
そうです。callメソッドです!
なので、下みたいにすれば実行できます。
greeting = Proc.new { 'おはよー!' }
greeting.call #=> 'おはよー!'
もちろん引数も渡せます!
greeting = Proc.new {|text1, text2| text1 + text2 }
greeting.call('おはよー!', 'からのこんばんはー!') #=> 'おはよー!からのこんばんはー!'
まとめると、Procオブジェクトはブロックそのもの。a = Proc.new {} した時のaはProcオブジェクトの保存先のメモリアドレスが入っているだけなので、実行する際は、a.callのように呼び出す。
Procオブジェクトを引数に渡す
ブロックを引数に渡す方法を思い出しましょう。
def greeting(&b)
puts 'おはよー!'
text = b.call('こんばんはー') # ブロックの処理内容を実行
puts text
end
# ブロックを引数に渡して実行
greeting do |text|
puts "#{text}!!"
end
おはよー!
こんばんはー!!
ここで重要なポイントは、引数に渡しているものはブロックそのものであるということ。ブロックそのものを渡す際は、&(アンパサンド)が必須です。でなければ、Rubyはブロックを普通の引数として認識してしまいます。また、ブロックは一つのメソッドに一つまでしか渡せません。
これらを解決するのがProcオブジェクト!
ブロックの代わりにProcオブジェクトを渡すことを考えてみましょう。
オブジェクトを渡すのであれば普通の引数で渡せますね!てことで&が必要なくなります。
また、一つのメソッドに一つまでの制限もなくなります。
では、実際にProcオブジェクトを渡した場合のコードを見てみましょう。
def greeting(b)
puts 'おはよー!'
text = b.call('こんばんはー!', 'せい!') # ブロックの処理内容を実行
puts text
end
# ブロックを引数に渡して実行
greeting_proc = Proc.new {|text1, text2| puts text1 + text2 }
greeting(greeting_proc)
おはよー!
こんばんはー!せい!
どうですか?Procオブジェクトがなんとなく理解できましたか?
まだクリアになっていない方はこの章を何度か読み直してみてください!
ラムダ式
最後の章です!
後少し頑張ってください!
ラムダ式はコンピュータサイエンスの基礎であり、JavaScriptなどでも登場する呼び出し可能オブジェクトです。
ラムダ式に関して説明しようと思うとそれだけで記事が書けてしまうので、ここではProcとの絡みにのみ着目してお話します。
ラムダ式とは、以下のような表記のことです!
->(a, b){ a + b }
or
lambda {|a, b| a + b }
そしてこの表記で何ができるかというと、Procと同じくブロックのオブジェクトが作れます。
ここでProcの表記と比較してみます。
意味は一緒です!
block_obj = Proc.new {|a, b| a + b }
block_obj = ->(a, b){ a + b }
二つの挙動に細かい違いはありますが、まず使えるようになることが目的であればまずはこれでオッケーです。
では最後に例のごとくgreetingメソッドを使ってラムダ式を実践してみましょう!
def greeting(b)
puts 'おはよー!'
text = b.call('こんばんはー')
puts text
end
block_obj = ->(text){ puts "#{text}!!" }
greeting(block_obj)
'おはよー!'
'こんばんはー!!'
#まとめ
ブロックとyieldとcallとProcとラムダ式について学んできました。
この辺りは未経験にとっての登竜門で、避けて通ってきた未経験の方やエンジニア1年生の方も多いのではないでしょうか。
Railsなどのフレームワークを学ぶことはもちろん大事ですが、それと同じかそれ以上にコンピュータサイエンスの基礎や、言語の理解を深めることは大事だと思っています!
この記事が、皆様がそういった基礎概念に目を向けるきっかけになれば幸いです。
Twitterで「未経験の方・エンジニア1年目の方に向けて、エンジニア1年目の学びを発信」してますので、興味がある方は是非のぞいてみてください!
では長らくお付き合いいただきありがとうございました!
また、次の記事でお会いしましょう〜!👋
-END-