RubyのProcとlamdaについてかく。
ブロックをオブジェクト化したもの。ブロックはオブジェクトではない。
このオブジェクトは手続きオブジェクトと呼ばれ、Procクラスのインスタンスとして表現される。
Proc.newまたはprocによって手続きオブジェクトを生成できる。
# Proc.new
proc_object = Proc.new { |x| puts x }
proc_object.call(1) # => 1
# proc
proc_object = proc{ |x| puts x }
proc_object.call(1) # => 1
Procオブジェクトを作成するための構文で、手続きオブジェクトを生成する。
lamdaまたは->{}によって手続きオブジェクトを生成できる。
# lamda
lamda_object = lambda { |x| puts x }
lamda_object.call(1) # => 1
# ->{}
lamda_object = ->(x) { puts x }
lamda_object.call(1) # => 1
Procの場合は緩いが、lamdaの場合は厳格である。
# Proc
proc_object = Proc.new { |x, y| puts "#{x}, #{y.inspect}" }
proc_object.call(10) #=> 10, nil
# lamda
lamda_object = lambda { |x, y| puts "#{x}, #{y.inspect}" }
lamda_object.call(10) #=> wrong number of arguments (given 1, expected 2) (ArgumentError)
次のような違いがある。
return | next | break | |
---|---|---|---|
Proc.new | メソッドを抜ける | 手続きオブジェクトを抜ける | 例外が発生する |
proc | メソッドを抜ける | 手続きオブジェクトを抜ける | 例外が発生する |
lambda | 手続きオブジェクトを抜ける | 手続きオブジェクトを抜ける | 手続きオブジェクトを抜ける |
# return
def proc_return
# ここでメソッドを抜ける
p = Proc.new { return "return from proc" }
result = p.call
return "#{result} method finished"
end
def proc_return_with_proc
# ここでメソッドを抜ける
p = proc { return "return from proc with proc" }
result = p.call
return "#{result} method finished"
end
def lambda_return
l = lambda { return "return from lambda" }
result = l.call
return "#{result} method finished"
# ここでメソッドを抜ける
end
puts proc_return # return from proc
puts proc_return_with_proc # return from proc with proc
puts lambda_return # return from lambda method finished
# next
def proc_next
p = Proc.new { return "return from proc" }
result = p.call
return "return from method"
# ここでメソッドを抜ける
end
def proc_next_with_proc
p = Proc.new { return "return from proc with proc" }
result = p.call
return "return from method"
# ここでメソッドを抜ける
end
def lamda_next
l = lambda { return "return from lambda" }
result = l.call
return "return from method"
# ここでメソッドを抜ける
end
puts proc_next # => return from proc
puts proc_next_with_proc # => return from proc with proc
puts lamda_next # => return from method
# break
def proc_break
p = Proc.new { break "Proc.new break" } # => LocalJumpError: break from proc-closure
result = p.call
puts "result: #{result}"
end
def proc_break_with_proc
proc_object = proc { break "proc break" } # => LocalJumpError: break from proc-closure
result = proc_object.call
puts "result: #{result}"
end
def lamda_break
l = lambda { break "lambda break" }
result = l.call
puts "result: #{result}"
end
puts proc_break # => LocalJumpError: break from proc-closure
puts proc_break_with_proc # => LocalJumpError: break from proc-closure
puts lamda_break # => result: lamda break
jp.quora.com - rubyのブロックとProcに本質的な違いはありますか?より引用。
補足として、歴史的な経緯を記述しておくと、ブロック(とyieldによる呼び出し)がまず最初に導入され、それから、それをオブジェクトするために Proc.new が誕生しました。また、無名関数相当を実現するために lambda メソッド(と proc メソッド)が導入されました。
その後、ブロック引数(&引数)が導入されることで Proc.new の必要性が薄れ、lambda式(->)が導入されることで、lambdaやprocメソッドの必要性が薄れ、それらは現在ではほとんど使われなくなっています。たぶん、将来これらのメソッドは削除されるでしょう。Rubyの歴史による変化を感じさせますね。
5年前の回答ではあるが、lamda式を使っておけば大抵問題ないということだろうか。
手続きオブジェクトがメソッドの外で使用されるようなケースにおいても違いがある。
return | next | break | |
---|---|---|---|
Proc.new | 例外が発生する | 手続きオブジェクトを抜ける | 例外が発生する |
proc | 例外が発生する | 手続きオブジェクトを抜ける | 例外が発生する |
lambda | 手続きオブジェクトを抜ける | 手続きオブジェクトを抜ける | 手続きオブジェクトを抜ける |
def orphan_proc
orphan = Proc.new { return "I'm an orphan!" }
return orphan
end
def orphan_proc_with_proc
orphan = proc { return "I'm an orphan!" }
return orphan
end
def orphan_lamda
orphan = lambda { return "I'm an orphan!" }
return orphan
end
puts orphan_proc.call # => LocalJumpError
puts orphan_proc_with_proc.call # => LocalJumpError
puts orphan_lamda.call # => "I'm an orphan
関連書籍