Ruby 1.8では、一方でproc / lambdaと他方で微妙な違いがありますProc.new
。
- それらの違いは何ですか?
- どれを選択するかを決める方法についてのガイドラインを教えてください。
- Ruby 1.9では、procとlambdaが異なります。どうしたんだ?
Ruby 1.8では、一方でproc / lambdaと他方で微妙な違いがありますProc.new
。
回答:
で作成されprocsの間にもう一つの重要な微妙な違いlambda
を使用して作成し、procsのはProc.new
、彼らがどのように処理するかであるreturn
文を:
lambda
プロシージャでは、return
ステートメントはプロシージャ自体からのみ返されますProc.new
プロシージャでは、return
ステートメントは少し意外です。これは、プロシージャからだけでなく、プロシージャを囲むメソッドからも制御を返します。これがlambda
-created procの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
-created proc return
が同じことをしているところです。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
ときよりも使用することを好む傾向があります。
proc
方法もあります。それは単に省略形Proc.new
ですか?
proc
同等ですProc.new
proc
のような行為lambda
はないようにProc.new
return文に関して。つまり、ruby docは不正確です。
proc
ようlambda
に動作しますがProc.new
、1.9のように動作します。Peter Wagenetの回答を参照してください。
lambda
は匿名メソッドです。これはメソッドであるため、値を返し、それを呼び出したメソッドは、それを無視して別の値を返すことを含め、必要に応じてそれを実行できます。A Proc
は、コードスニペットの貼り付けに似ています。メソッドのようには動作しません。したがって、内でリターンが発生した場合、Proc
それはそれを呼び出したメソッドのコードの一部にすぎません。
さらに明確にするために:
ジョーイは、の復帰行動Proc.new
は意外であると言います。ただし、Proc.newがブロックのように動作すると考えると、これはブロックの動作とまったく同じであるため、当然のことです。一方、ランバはメソッドのように動作します。
これは実際に、Procがアリティ(引数の数)に関して柔軟であるのに対し、ラムダは柔軟でない理由を説明しています。ブロックでは、すべての引数を指定する必要はありませんが、メソッドは必要です(デフォルトが指定されていない場合)。ラムダ引数のデフォルトを提供することはRuby 1.8ではオプションではありませんが、Ruby 1.9では代替のラムダ構文(webmatで言及されている)でサポートされるようになりました。
concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1) # => "12"
そして、Michiel de Mare(OP)は、Ruby 1.9のアリティで同じように動作するProcsとlambdaについては正しくありません。上記のように1.8からの動作を維持していることを確認しました。
break
ステートメントは実際にはProcsでもラムダでもあまり意味がありません。Procsでは、ブレークにより、既に完了しているProc.newから戻ります。そして、ラムダは本質的にメソッドなので、ラムダからブレークしても意味がなく、メソッドのトップレベルからブレークすることは決してありません。
next
、redo
、およびraise
手続きオブジェクトおよびラムダの両方で同じように動作。一方retry
、どちらでも許可されておらず、例外が発生します。
最後に、このproc
メソッドは一貫性がなく、予期しない動作をするため、使用しないでください。Ruby 1.8では、実際にはラムダを返します!Ruby 1.9ではこれが修正され、Procが返されます。Procを作成する場合は、を使用しProc.new
ます。
詳細については、この情報のほとんどの情報源であるオライリーのRubyプログラミング言語を強くお勧めします。
break
手続きオブジェクトから上昇させるLocalJumpError
のに対し、break
同じようラムダの挙動からreturn
(すなわち、return nil
)。
私が見つかりました。このページの間のショーどのような違いをProc.new
してlambda
います。このページによると、唯一の違いは、ラムダが受け入れる引数の数について厳密であるのに対し、Proc.new
欠落している引数をに変換することnil
です。違いを示すIRBセッションの例を次に示します。
irb(main):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" => "helloworld" irb(main):004:0> p.call "hello"、 "world" => "helloworld" irb(main):005:0> l.call "hello" ArgumentError:引数の数が正しくありません(1対2) (irb):1から from(irb):5:in `call ' (irb)から:5 から:0 irb(main):006:0> p.call "hello" TypeError:nilを文字列に変換できません from(irb):2:in `+ ' (irb)から:2 from(irb):6:in `call ' (irb)から:6 から:0
このページでは、特にエラー耐性のある動作が必要でない限り、ラムダの使用を推奨しています。私はこの感情に同意します。ラムダを使用する方が少し簡潔に思え、そのようなわずかな違いで、平均的な状況ではより良い選択のようです。
Ruby 1.9については、申し訳ありませんが、まだ1.9を調べていませんが、それほど変更されているとは思いません(ただし、私の言葉を使わないでください。いくつかの変更を聞いたことがあるようです。私はおそらく間違っています)。
Procは古いですが、returnのセマンティクスは私には非常に直観に反しています(少なくとも私が言語を学んでいたとき)。
Lambdaは機能的に安全であり、推論が容易です。私は常にprocの代わりにそれを使用しています。
微妙な違いについてはあまり言えません。ただし、Ruby 1.9ではラムダとブロックのオプションのパラメーターが許可されていることを指摘できます。
これは1.9以下のスタビーラムダの新しい構文です:
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をビルドする場合は、この記事(恥知らずなセルフプロモーション)をチェックしてください。
短い答え:重要なのは何をするかreturn
です:lambdaはそれ自体から戻り、procはそれ自体とそれを呼び出した関数から戻ります。
あまり明確でないのは、それぞれを使用する理由です。ラムダは、関数型プログラミングの意味で私たちがすべきことを期待しています。これは基本的に、現在のスコープが自動的にバインドされる匿名メソッドです。2つのうち、ラムダはおそらく使用すべきものです。
一方、Procは、言語自体を実装するのに非常に役立ちます。たとえば、「if」ステートメントまたは「for」ループを実装できます。プロシージャで見つかった戻り値は、それを呼び出したメソッドから返されます。「if」ステートメントだけではありません。これが言語のしくみであり、 "if"ステートメントのしくみでもあるので、Rubyはこれを裏で使用していて、強力なように見えただけだと思います。
ループやif-else構成などの新しい言語構成を作成する場合にのみ、これが本当に必要になります。
Questonの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
proc
1.8でラムダを返しました。1.9ではprocを返すように修正されましたが、これは重大な変更です。したがって、
Rubyのクロージャーは、Rubyでのブロック、ラムダ、プロシージャの動作の概要です。
ラムダは他の言語と同様に期待どおりに動作します。
有線Proc.new
は意外で紛らわしいです。
return
によって作成されたproc のステートメントはProc.new
、それ自体からだけでなく、それを囲むメソッドからも制御を返します。
def some_method
myproc = Proc.new {return "End."}
myproc.call
# Any code below will not get executed!
# ...
end
Proc.new
ブロックのように、それがコードを囲んでいるメソッドに挿入することを主張できます。ただしProc.new
、オブジェクトを作成しますが、ブロックはオブジェクトの一部です。
また、ラムダとには別の違いがありProc.new
ます。それは、(間違った)引数の処理です。lambdaはそれについて文句を言う一方、Proc.new
余分な引数を無視するか、引数がないことをnilと見なします。
irb(main):021:0> l = -> (x) { x.to_s }
=> #<Proc:0x8b63750@(irb):21 (lambda)>
irb(main):022:0> p = Proc.new { |x| x.to_s}
=> #<Proc:0x8b59494@(irb):22>
irb(main):025:0> l.call
ArgumentError: wrong number of arguments (0 for 1)
from (irb):21:in `block in irb_binding'
from (irb):25:in `call'
from (irb):25
from /usr/bin/irb:11:in `<main>'
irb(main):026:0> p.call
=> ""
irb(main):049:0> l.call 1, 2
ArgumentError: wrong number of arguments (2 for 1)
from (irb):47:in `block in irb_binding'
from (irb):49:in `call'
from (irb):49
from /usr/bin/irb:11:in `<main>'
irb(main):050:0> p.call 1, 2
=> "1"
ところで、proc
Ruby 1.8ではラムダを作成しますが、Ruby 1.9+ではのように動作しますがProc.new
、これは本当に混乱します。
私はこれに少し遅れていますがProc.new
、コメントで言及されていないことについて、1つの素晴らしいがほとんど知られていないことが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!
&block
引数を宣言するのと同じことdef
ですが、def argリストでそれを行う必要はありません。
return
procでは、字句的に囲んでいるメソッド、つまりprocを呼び出したメソッドではなく、procが作成されたメソッドから戻ることを強調する価値があります。これは、プロシージャのクロージャプロパティの結果です。したがって、次のコードは何も出力しません。
def foo
proc = Proc.new{return}
foobar(proc)
puts 'foo'
end
def foobar(proc)
proc.call
puts 'foobar'
end
foo
procはで実行されますがfoobar
、それはで作成されたfoo
ため、単にreturn
終了するのfoo
ではなく、終了しますfoobar
。Charles Caldwellが上で書いたように、それはそれにGOTOの感じを持っています。私の意見でreturn
は、レキシカルコンテキストで実行されるブロックでは問題ありませんが、別のコンテキストで実行されるprocで使用すると、直感性が大幅に低下します。
との動作の違いはreturn
、IMHOと2の最も重要な違いです。Proc.newよりも入力が少ないため、ラムダも好みます:-)
proc {}
。これがいつ有効になったかはわかりませんが、Proc.newと入力するよりも(少し)簡単です。