Ruby 1.8 では、proc/lambda との間に微妙な違いがありProc.new
ます。
- それらの違いは何ですか?
- どちらを選択するかを決定する方法について、ガイドラインを教えていただけますか?
- Ruby 1.9 では、proc と lambda が異なります。どうしたんだ?
lambda
で作成されたprocとで作成されたprocのもう1つの重要ですが微妙な違いは、ステートメントProc.new
の処理方法です。return
lambda
-created procでは、ステートメントreturn
はproc自体からのみ返されますProc.new
-created procでは、return
ステートメントはもう少し驚くべきものです。procからだけでなく、procを囲むメソッドからも制御を返します。lambda
これが-createdprocの動作return
です。それはおそらくあなたが期待する方法で動作します:
def whowouldwin
mylambda = lambda {return "Freddy"}
mylambda.call
# mylambda gets called and returns "Freddy", and execution
# continues on the next line
return "Jason"
end
whowouldwin
#=> "Jason"
これがProc.new
-createdprocreturn
が同じことをしているところです。Rubyが驚き最小の原則を破るケースの1つを目にするところです。
def whowouldwin2
myproc = Proc.new {return "Freddy"}
myproc.call
# myproc gets called and returns "Freddy",
# but also returns control from whowhouldwin2!
# The line below *never* gets executed.
return "Jason"
end
whowouldwin2
#=> "Freddy"
この驚くべき振る舞い(およびタイピングの減少)のおかげで、私はprocを作成するときに使用lambda
することを好む傾向がProc.new
あります。
との違いを示すこのページを見つけました。ページによると、唯一の違いは、ラムダが受け入れる引数の数について厳密であるのに対し、欠落している引数を に変換することです。違いを示す IRB セッションの例を次に示します。Proc.new
lambda
Proc.new
nil
irb(メイン):001:0> l = ラムダ { |x, y| x + y } => #<Proc:0x00007fc605ec0748@(irb):1> irb(main):002:0> p = Proc.new { |x, y| x + y } => #<Proc:0x00007fc605ea8698@(irb):2> irb(main):003:0> l.call "hello", "world" =>「ハローワールド」 irb(main):004:0> p.call "hello", "world" =>「ハローワールド」 irb(main):005:0> l.call "hello" ArgumentError: 引数の数が間違っています (1 対 2) (irb) から:1 from (irb):5:in `call' から (irb):5 から:0 irb(メイン):006:0> p.call "こんにちは" TypeError: nil を String に変換できません from (irb):2:in `+' から (irb):2 from (irb):6:in `call' から (irb):6 から:0
このページでは、特にエラー トレラントな動作が必要でない限り、ラムダの使用も推奨しています。私はこの感情に同意します。ラムダを使用すると、少し簡潔に見えますが、そのようなわずかな違いがあるため、平均的な状況ではより良い選択のようです。
Ruby 1.9 については、申し訳ありませんが、まだ 1.9 を調べていませんが、それほど変更されるとは思いません (私の言葉を鵜呑みにしないでください。いくつかの変更について聞いたことがあるようですので、私はおそらくそこで間違っています)。
Proc は古いですが、return のセマンティクスは私にとって非常に直感に反するものです (少なくとも私が言語を学んでいたとき)。
ラムダは機能的により安全で、理由を簡単に説明できます。私は常に proc の代わりにラムダを使用しています。
微妙な違いはなんとも言えません。ただし、Ruby 1.9 では、ラムダとブロックのオプションのパラメーターが許可されるようになったことを指摘できます。
1.9 未満の stabby ラムダの新しい構文は次のとおりです。
stabby = ->(msg='inside the stabby lambda') { puts msg }
Ruby 1.8 にはその構文がありませんでした。ブロック/ラムダを宣言する従来の方法も、オプションの引数をサポートしませんでした。
# under 1.8
l = lambda { |msg = 'inside the stabby lambda'| puts msg }
SyntaxError: compile error
(irb):1: syntax error, unexpected '=', expecting tCOLON2 or '[' or '.'
l = lambda { |msg = 'inside the stabby lambda'| puts msg }
ただし、Ruby 1.9 では、古い構文でもオプションの引数がサポートされています。
l = lambda { |msg = 'inside the regular lambda'| puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez
Leopard または Linux 用の Ruby1.9 をビルドしたい場合は、この記事(恥知らずな自己宣伝) を参照してください。
これを確認する良い方法は、ラムダが独自のスコープで (メソッド呼び出しであるかのように) 実行されることですが、Procs は呼び出し元のメソッドとインラインで実行されると見なすことができます。少なくとも、どちらを使用するかを決定する良い方法です。いずれの場合にも。
簡単な答え: 重要なのは何をするかreturn
です: lambda はそれ自体から戻り、proc はそれ自体とそれを呼び出した関数から戻ります。
あまり明確でないのは、それぞれを使用する理由です。ラムダは、関数型プログラミングの意味で物事が行うべきだと私たちが期待するものです。これは基本的に、現在のスコープが自動的にバインドされた匿名メソッドです。2 つのうち、おそらくラムダを使用する必要があります。
一方、Proc は、言語自体の実装に非常に役立ちます。たとえば、「if」ステートメントや「for」ループを実装できます。proc で見つかった return は、"if" ステートメントだけでなく、それを呼び出したメソッドからも返されます。これは言語がどのように機能し、「if」ステートメントがどのように機能するかです。私の推測では、Ruby はこれを隠れて使用し、強力に見えたので公開しただけです。
これは、ループや if-else 構造などの新しい言語構造を作成する場合にのみ必要です。
クエストの3番目のメソッドである「proc」についてはコメントがありません。これは非推奨ですが、1.8と1.9では処理が異なります。
これはかなり冗長な例で、3つの類似した呼び出しの違いを簡単に確認できます。
def meth1
puts "method start"
pr = lambda { return }
pr.call
puts "method end"
end
def meth2
puts "method start"
pr = Proc.new { return }
pr.call
puts "method end"
end
def meth3
puts "method start"
pr = proc { return }
pr.call
puts "method end"
end
puts "Using lambda"
meth1
puts "--------"
puts "using Proc.new"
meth2
puts "--------"
puts "using proc"
meth3
Closures in Rubyは、ブロック、ラムダ、およびプロシージャが Ruby で Ruby を使用してどのように機能するかについての概要です。
アコーディオンガイの応答について詳しく説明するには:
Proc.new
ブロックが渡されることによってprocoutを作成することに注意してください。lambda {...}
これは、ブロックを渡すメソッド呼び出しではなく、一種のリテラルとして解析される と思います。return
メソッド呼び出しにアタッチされたブロックの内側から実行すると、ブロックではなくメソッドから返されますProc.new
。ケースは、この例です。
(これは1.8です。これが1.9にどのように変換されるかはわかりません。)
私はこれに少し遅れていますがProc.new
、コメントでまったく言及されていないことについて、あまり知られていない素晴らしいことが1つあります. ドキュメントによると:
Proc::new
は、ブロックが添付されたメソッド内でのみ、ブロックなしで呼び出すことができます。この場合、そのブロックはProc
オブジェクトに変換されます。
そうは言っても、Proc.new
yielding メソッドを連鎖させましょう。
def m1
yield 'Finally!' if block_given?
end
def m2
m1 &Proc.new
end
m2 { |e| puts e }
#⇒ Finally!
との動作の違いreturn
は、私見ですが、2つの間の最も重要な違いです。Proc.newよりも入力が少ないため、ラムダも好みます:-)