Rubyのlambdaは何をしているのか?

2022.06.16

lambdaとは?

与えられたブロックから手続きオブジェクト (Proc のインスタンス) を生成して返します。Proc.new に近い働きをします。

Ruby 3.1 リファレンスマニュアル

おさらいで、ブロックとはdo~endや{}で囲まれた処理のことを指す。手続きオブジェクトはprocやlambdaによってブロックをオブジェクトにしたもの。

使い方はProc.newとほぼ同じ。Proc.newについてはこちらの記事で。

なぜ作られた?(存在意義)

procを作った後にジャンプ構文(next,break,returnなど)の挙動でprocとは違う挙動をするものが欲しくなったから。予想。

lambdaを使った時と使わない時の違い

lambdaを使わない書き方。帰る方法を増やしたい時に条件文が肥大する。

def back_home(is_fast)
 puts "駅に行く"
 if is_fast
  bullet_train
 else
  local_train
 end
 puts "家まで歩く"
end

def bullet_train
 puts "終着まで寝る"
 puts "16:30分に駅に着く"
end

def local_train
 puts "2回乗り換える"
 puts "18:00時に駅に着く"
end

back_home(false)

lambdaを使った書き方。if文が必要なくback_homeメソッドがこれ以上大きくならない。手続きオブジェクトを作ることで複数の帰る方法を設定できる。

def back_home(train) 
 puts "駅に行く" 
 train.call  
 puts "家まで歩く"  
end 
 
bullet_train = lambda { 
 puts "終着まで寝る" 
 puts "16:30分に駅に着く" 
} 
 
local_train = lambda { 
 puts "2回乗り換える" 
 puts "18:00時に駅に着く" 
}

back_home(local_train)

Proc.newとlambdaの違い

引数の扱いが違う

lambda のほうがより厳密です。引数の数が違っていると(メソッドのように)エラーになります。 Proc.new は引数を多重代入に近い扱い方をします。

Ruby 3.1 リファレンスマニュアル

procはブロックと同じ挙動をする。

# procの場合
a = Proc.new {|x,y|
 p x,y
}
a.call(1)
#=> 1
#=> nil

# ブロック
[1].map do |x, y|
 p n
 p i
end
#=> 1
#=> nil

lambdaの方が引数の扱いが厳密でメソッドと同じような扱いになる。

# lambdaの場合
b = lambda {|x,y|
 p x,y
}
b.call(1,2,3)
#=> wrong number of arguments (given 3, expected 2) (ArgumentError)

# メソッドの場合
def c(x,y)
 p x,y
end
c(1,2,3)
#=> wrong number of arguments (given 3, expected 2) (ArgumentError)

returnとbreakの挙動が違う

return と break は、lambda と Proc.new では挙動が異なります。例えば return を行った場合、lambda では手続きオブジェクト自身を抜けますが、 Proc.new では手続きオブジェクトを囲むメソッドを抜けます。

Ruby 3.1 リファレンスマニュアル

returnとbreakをつけた時の挙動が変わる。

  • lambda・・・手続きオブジェクト自体から抜ける
  • Proc.new ・・・手続きオブジェクトを囲んでいるメソッドから抜ける
def back_home(train)
 puts "駅に行く"
 train.call
 puts "家まで歩く"
end

bullet_train = lambda {
 puts "終着まで寝る"
 return puts "16:30分に駅に着く"
}

local_train = Proc.new {
 puts "2回乗り換える"
 return puts "18:00時に駅に着く"
}

back_home(bullet_train)
#=> 駅に行く
#=> 終着まで寝る
#=> 16:30分に駅に着く
#=> 家まで歩く

back_home(local_train)
#=> 駅に行く
#=> 2回乗り換える
#=> 18:00分に駅に着く

まとめ

lambda・・returnやbreakを使っても手続きオブジェクトを囲んでいるメソッドから抜けない。だからブロック内での処理を抜けたいがメソッドからは抜けたくない時に使える。

proc・・returnやbreakを使うと手続きオブジェクトを囲んでいるメソッドから抜ける。だからブロック内での処理結果によってメソッドごと抜けたい時に使える。

引数を厳密に扱いたい場合はlambdaがおすすめ。