Rubyのprocとlambdaの違いは何ですか?


回答:


260

1つの違いは、引数の処理方法です。proc {}およびを使用しProc.new {}てプロシージャを作成することは同等です。ただし、を使用lambda {}すると、渡された引数の数をチェックするプロシージャが提供されます。からri Kernel#lambda

結果のProcオブジェクトが呼び出されたときに渡されたパラメーターの数をチェックすることを除いて、Proc.newと同等です。

例:

p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:0x3c7d28@(irb):1>
p.call 1, 2 # => 5
p.call 1 # => NoMethodError: undefined method `**' for nil:NilClass
p.call 1, 2, 3 # => 5
l = lambda {|a, b| puts a**2+b**2 } # => #<Proc:0x15016c@(irb):5 (lambda)>
l.call 1, 2 # => 5
l.call 1 # => ArgumentError: wrong number of arguments (1 for 2)
l.call 1, 2, 3 # => ArgumentError: wrong number of arguments (3 for 2)

さらに、Kenが指摘しているreturnように、ラムダ内で使用するとそのラムダの値が返されますが、プロシージャで使用すると、外側のreturnブロックから返されます。

lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return

したがって、ほとんどの迅速な使用については同じですが、自動の厳密な引数チェックが必要な場合(デバッグに役立つ場合もあります)、またはreturnステートメントを使用してプロシージャの値を返す必要がある場合は、を使用しますlambda


8
ラムダはメソッドと非常によく似ていますが(引数をチェックしてリターンはそれらから返されます)、プロシージャはブロックと非常によく似ています(引数はチェックされず、戻り値は含まれているメソッドまたはラムダから返されます)。
ペッツ2014年

私は神に行ったことがあるので、これまでにいくつのウェブサイトや記事を知っていますか?すべての説明は、戻り値などがどのように異なるかについての詳細を提供するだけですが、それが重要である理由については説明しません。今のところ、これはRubyの設計上の混乱であると結論づける必要があります。
ankush981

76

procsとlambdasの本当の違いは、制御フローのキーワードと関係があります。私は話していますreturnraisebreakredoretryこれらの制御ワード-など。プロシージャにreturnステートメントがあるとします。あなたがあなたのprocを呼び出すとき、それはあなたをそれからダンプするだけでなく、囲んでいるメソッドからも戻ります:例えば:

def my_method
  puts "before proc"
  my_proc = Proc.new do
    puts "inside proc"
    return
  end
  my_proc.call
  puts "after proc"
end

my_method

shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc

putsメソッドのfinal は決して実行されませんでした。なぜなら、私たちがprocを呼び出したとき、そのreturn内部でメソッドからダンプされたからです。ただし、プロシージャをラムダに変換すると、次のようになります。

def my_method
  puts "before proc"
  my_proc = lambda do
    puts "inside proc"
    return
  end
  my_proc.call
  puts "after proc"
end

my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
after proc

ラムダ内のリターンはラムダ自体からダンプするだけであり、囲んでいるメソッドは実行を続けます。制御フローキーワードがprocとlambda内で処理される方法は、それらの間の主な違いです


7

主な違いは2つだけです。

  • まず、a lambdaは渡された引数の数をチェックしますが、aはチェックprocしません。これはlambda、間違った数の引数を渡した場合、a はエラーをスローしますが、a procは予期しない引数を無視nilし、不足している引数に割り当てます。
  • 第2に、lambdaリターンが返されると、呼び出し元のメソッドに制御が戻されます。がproc戻ると、呼び出し元のメソッドに戻ることなく、すぐに戻ります。

これがどのように機能するかを確認するには、以下のコードを見てください。最初のメソッドはを呼び出しprocます。2番目はを呼び出しますlambda

def batman_ironman_proc
  victor = Proc.new { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

puts batman_ironman_proc # prints "Batman will win!"

def batman_ironman_lambda
  victor = lambda { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

puts batman_ironman_lambda # prints "Iron Man will win!"

procが「バットマンが勝つ!」と言うのを見てください。これは、batman_ironman_procメソッドに戻らずにすぐに戻るためです。

私たちはlambda、しかし、呼び出された後、バック方式に移行し、その方法は、それが評価され、最後のコードを返します:「アイアンマンは勝ちます!」


5

#プロシージャの例

p = Proc.new { |x| puts x*2 }
[1,2,3].each(&p)              # The '&' tells ruby to turn the proc into a block 

proc = Proc.new { puts "Hello World" }
proc.call

#ラムダの例

lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)

lam = lambda { puts "Hello World" }
lam.call           

ProcsとLambdasの違い

プロシージャとラムダの違いに入る前に、両方がProcオブジェクトであることを説明することが重要です。

proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }

proc.class # returns 'Proc'
lam.class  # returns 'Proc'

ただし、ラムダはprocの別の「フレーバー」です。このわずかな違いは、オブジェクトを返すときに表示されます。

proc   # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam    # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'

1.ラムダは引数の数をチェックしますが、プロシージャはチェックしません

lam = lambda { |x| puts x }    # creates a lambda that takes 1 argument
lam.call(2)                    # prints out 2
lam.call                       # ArgumentError: wrong number of arguments (0 for 1)
lam.call(1,2,3)                # ArgumentError: wrong number of arguments (3 for 1)

対照的に、プロシージャは、間違った数の引数が渡されても問題ありません。

proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(2)                   # prints out 2
proc.call                      # returns nil
proc.call(1,2,3)               # prints out 1 and forgets about the extra arguments

2.ラムダとプロシージャは 'return'キーワードを異なる方法で処理します

ラムダ内の 'return'は、ラムダコードのすぐ外側でコードをトリガーします

def lambda_test
  lam = lambda { return }
  lam.call
  puts "Hello world"
end

lambda_test                 # calling lambda_test prints 'Hello World'

プロシージャ内の「return」は、プロシージャが実行されているメソッドの外部のコードをトリガーします

def proc_test
  proc = Proc.new { return }
  proc.call
  puts "Hello world"
end

proc_test                 # calling proc_test prints nothing

そして、他のクエリに答えるために、どれをいつ使用しますか?彼が言ったように私は@jtbandesに従います

したがって、ほとんどの迅速な使用法については同じですが、自動で厳密な引数チェックが必要な場合(デバッグにも役立つ場合があります)、またはreturnステートメントを使用してプロシージャの値を返す必要がある場合は、lambdaを使用します。

もともとここに投稿


1

一般的に、ラムダはメソッドに似ているため、プロシージャよりも直感的です。アリティについてはかなり厳格で、returnを呼び出すと単に終了します。このため、Procの特定の機能が必要でない限り、多くのRubyistは最初の選択肢としてラムダを使用します。

Procs:クラスのオブジェクトProc。ブロックと同様に、それらは定義されたスコープで評価されます。 ラムダ:クラスのオブジェクトでもありますがProc、通常のプロシージャとは微妙に異なります。それらはブロックやプロシージャのようなクロージャであり、したがって、それらは定義されたスコープで評価されます。

プロシージャの作成

a = Proc.new { |x| x 2 }

ラムダを作成する

b = lambda { |x| x 2 }


a = proc { |x| x 2 }と同じa = Proc.new { |x| x 2 }
lacostenycoder

1

これを理解する別の方法を次に示します。

ブロックは、オブジェクトのメソッドの呼び出しの呼び出しに添付されたコードのチャンクです。以下の例では、selfはRailsフレームワーク(それ自体に多くのヘルパーモジュールが含まれています)のActionView :: Baseを継承する匿名クラスのインスタンスです。カードは私たちが自分に呼びかける方法です。メソッドに引数を渡してから、メソッド呼び出しの最後に常にブロックをアタッチします。

self.card :contacts do |c|
  // a chunk of valid ruby code    
end

では、メソッドのコードを渡します。しかし、このブロックをどのように利用するのでしょうか。1つのオプションは、コードのチャンクをオブジェクトに変換することです。Rubyはコードのチャンクをオブジェクトに変換する3つの方法を提供します

# lambda
> l = lambda { |a| a + 1 }
> l.call(1)
=> 2 

# Proc.new
> l2= Proc.new { |a| a + 1 }
> l2.call(1)
=> 2 

# & as the last method argument with a local variable name
def add(&block)
end

上記のメソッドでは、&はメソッドに渡されたブロックをオブジェクトに変換し、そのオブジェクトをローカル変数ブロックに格納します。実際、lambdaおよびProc.newと同じ動作であることを示すことができます。

def add(&block)
  block
end

l3 = add { |a| a + 1 }
l3.call(1)
=> 2

これは重要。ブロックをメソッドに渡し、&を使用して変換すると、ブロックが作成するオブジェクトはProc.newを使用して変換を実行します。

オプションとして「proc」の使用を避けたことに注意してください。これは、Ruby 1.8、ラムダと同じであり、Ruby 1.9ではProc.newと同じであり、すべてのRubyバージョンでは避ける必要があります。

それでは、ラムダとProc.newの違いは何ですか?

まず、パラメーターの受け渡しに関して、lambdaはメソッド呼び出しのように動作します。間違った数の引数を渡すと、例外が発生します。対照的に、Proc.newは並列割り当てのように動作します。未使用の引数はすべてnilに変換されます:

> l = lambda {|a,b| puts "#{a} + #{b}" }
 => #<Proc:0x007fbffcb47e40@(irb):19 (lambda)> 
> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)

> l2 = Proc.new {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb261a0@(irb):21> 
> l2.call(1)
1 + 

第2に、lambdaとProc.newは、returnキーワードを異なる方法で処理します。Proc.new内でreturnを実行すると、実際にはそれを囲んでいるメソッド、つまり周囲のコンテキストから戻ります。ラムダブロックから戻るときは、囲んでいるメソッドではなく、単にブロックから戻ります。基本的には、ブロックの呼び出しを終了し、残りの囲んでいるメソッドで実行を継続します。

> def add(a,b)
  l = Proc.new { return a + b}
  l.call
  puts "now exiting method"
end
> add(1,1)
=> 2  # NOTICE it never prints the message "now exiting method"

> def add(a,b)
  l = lambda { return a + b }
  l.call
  puts "now exiting method"
end
> add(1,1)
=> now exiting method  # NOTICE this time it prints the message "now exiting method"

それでは、なぜこの行動の違いがあるのでしょうか。その理由は、Proc.newを使用すると、メソッドを囲むコンテキスト内でイテレータを使用して、論理的な結論を引き出すことができるからです。この例を見てください:

> def print(max)
  [1,2,3,4,5].each do |val|
    puts val
    return if val > max
  end
end
> print(3)
1
2
3
4

イテレータ内でreturnを呼び出すと、それを囲んでいるメソッドから戻ることが期待されます。イテレータに渡されたブロックはProc.newを使用してオブジェクトに変換されるので、returnを使用すると、それが囲んでいるメソッドを終了するので注意してください。

ラムダは匿名メソッドと考えることができます。ラムダは、コードの個々のブロックを、メソッドのように扱うことができるオブジェクトに分離します。最終的に、ラムダはanomyousメソッドとして動作し、Proc.newはインラインコードとして動作するものと考えてください。



-3

procとlambdaの違いは、procは引数が順番に置き換えられたコードの単なるコピーであり、lambdaは他の言語と同様の関数であるということです。(戻り動作、引数チェック)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.