概要
Rubyのブロック・Proc・Lambdaを理解する上で必要な基礎知識をまとめました。
ブロックとは?
ブロックとはdo~end
もしくは{}
で囲まれた処理のカタマリの事を指します。
以下のコードのdo |a| p a end
, { |a| p a }
がブロックと呼ばれる部分です。
3.times do |a|
p a
end
#=> 0 1 2
3.times { |a| p a }
#=> 0 1 2
メソッドにブロックを渡す
メソッドの中でyield
を明示すると受け取ったブロックを実行します。
yieldがあるにも関わらずブロックが渡されない場合にはエラーが発生します。
def method1
yield
end
method1 { p 'block' } #=> "block"
method1 #=> `method1': no block given (yield) (LocalJumpError)
指定したメソッドにブロックが渡されたかどうかはblock_given?
を利用する事で確かめる事ができます。
def method1
p block_given?
end
method1 { p 'block' } #=> true
method1 #=> false
ここまでの知識を応用して、ブロックが渡された時には実行し、それ以外の場合でエラーの発生をさせたくない場合は以下の様に書けます。
def method1
yield if block_given?
end
method1 { p 'block' } #=> "block"
method1 ##エラーが発生しない
また、ブロックに引数を渡す事もできます。
以下のコードのように、ブロック内で設定した仮引数(parameter
)に対してyield
から実引数('arg'
)を渡す事が可能です。
def method2
yield('arg')
end
method2 { |parameter| p parameter } #=> "arg"
Procとは?
- Procとはブロックを持ち運び便利な
オブジェクト
にしたものです。 - Procはクラスなので
Proc.new
でオブジェクトを作る事が出来ます。 -
Proc.new
によって作成されたProcオブジェクトはcallで呼び出すことが出来ます。
proc1 = Proc.new { p "proc" }
##Proc.newによってブロック({ p "proc" })をオブジェクト化したproc1を作成。
proc1.call #=> "proc"
##Proc.newによって作成されたproc1を.callで呼び出す。
Procオブジェクト作成の際に以下のように仮引数を設定する事が可能です。
proc1 = Proc.new { |par1| p par1 }
proc1.call("arg") #=> "arg"
ブロックを引数で明示的に受け取る方法
メソッドにブロックを渡す方法としてyield
を紹介ましたが、下記コードのように&
から始まるブロック引数
を使用する事でブロックを引数で明示的に受け取ることも可能です。
下記コードでは以下のような工程を経ています。
&
が付いた引数(ブロック引数)に渡されたブロックをProcオブジェクト
に変換する。(ブロックのオブジェクト化)procオブジェクト.call
によって渡されたオブジェクトを呼び出す。
def method2(&proc) ##ブロックのオブジェクト化
proc.call ##Procオブジェクトの呼び出し
end
method2 { p 'block' } #=> "block"
lambdaとは?
lambda
とはProcオブジェクトを作る方法の一つで、lambdaメソッドによって作成されたProcオブジェクトはProc.new
で作成されたオブジェクトといくつか異なる点を持ちます。
Procの説明部分でProcオブジェクトを作成する方法の1つであるProc.new
を紹介しましたが、実際には以下の様な複数の方法が存在します。lambda
もその内の1つです。
-
Proc.new{ }
による方法 -
proc{ }
による方法 -
lambda{ }
による方法 -
->{ }
による方法(lambdaによって作成されたProcオブジェクトと同じ性質をもつオブジェクトを作成する。)
proc1 = Proc.new { |arg| puts arg } ## Proc.new{ } による方法
proc2 = proc { |arg| puts arg } ##proc{ } による方法
proc3 = lambda { |arg| puts arg } ##lambda{ } による方法
proc4 = ->(word) { puts word } ##->{ } による方法
proc5 = -> { puts "proc" } ##->{ } による方法
Proc.newとlambdaの相違点
lambda
によって作成されたProcオブジェクトとProc.new
によって作成されたProcオブジェクトの相違点は主に以下の2点です。lambda
によって作成された物の方がよりメソッドに近い働きをします。
- 引数チェック
- returnの挙動
相違点①(引数チェック)
Proc.newの場合
Proc.new
の場合は渡す引数が多いと、先頭から必要な数だけ取って後は無視し、少ないと足りない部分に nil を割り当てるため多重代入に近い扱い方をします。
proc1 = proc { |arg| p arg }
proc1.call( 'proc', 'lambda') #=> "proc"
lambdaの場合
lambda
の場合は引数の数が違うとArgumentErrorになります。
lamd = lambda { |arg| p arg }
lamd.call('lambda') #=> "lambda"
lamd.call('lambda', 'proc') #=> wrong number of arguments (given 2, expected 1) (ArgumentError)
相違点②(returnの挙動)
Proc.newの場合
Proc.newの場合はreturn後にメソッド自体を抜けてしまいます。
def proc_method
proc = Proc.new { return p "proc"}
proc.call
p "proc method"
end
proc_method #=> "proc"
lambdaの場合
lambdaの場合はreturnした後にメソッドに戻り、メソッドを最後まで実行します。
def lambda_method
lambda1 = lambda { return p "lambda"}
lambda1.call
p "lambda method"
end
lambda_method #=> "lambda"
#=> "lambda method"
参考
Ruby block/proc/lambdaの使いどころ
【Ruby】ブロックとProcとlambdaについて
Rubyの ブロック、Proc.new、lambdaの違い