lambda をいつ使用するか、Proc.new をいつ使用するか?
質問
Ruby 1.8 では、proc/lambda との間に微妙な違いがあります。 Proc.new
もう一方の。
- それらの違いは何ですか?
- どちらを選択するかを決定する方法についてのガイドラインを教えていただけますか?
- Ruby 1.9 では、proc と lambda は異なります。どうしたんだ?
解決
で作成されたプロシージャ間のもう 1 つの重要だが微妙な違い lambda
および で作成された proc Proc.new
彼らがどのように対処するかです return
声明:
- で
lambda
-作成されたプロシージャ、return
ステートメントはプロシージャ自体からのみ返されます - で
Proc.new
-作成されたプロシージャ、return
ステートメントはもう少し驚くべきものです:プロシージャからだけでなく制御を返します。 プロシージャを囲むメソッドからも同様です。
こちらです lambda
-作成されたプロシージャ 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
-作成されたプロシージャ 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"
この驚くべき動作 (および入力の軽減) のおかげで、私は を使用することを好む傾向があります。 lambda
以上 Proc.new
プロシージャを作成するとき。
他のヒント
さらに説明するには:
ジョーイは、戻ってくる行動は、 Proc.new
驚くべきことだ。ただし、Proc.new がブロックのように動作することを考えると、それがまさにブロックの動作であるため、これは驚くべきことではありません。一方、lamba はメソッドのように動作します。
これは、アリティ (引数の数) に関して Proc が柔軟であるのに対し、ラムダが柔軟ではない理由を実際に説明しています。ブロックではすべての引数を指定する必要はありませんが、メソッドでは必要です (デフォルトが指定されている場合を除く)。デフォルトでラムダ引数を指定することは Ruby 1.8 ではオプションではありませんが、Ruby 1.9 では代替ラムダ構文を使用してサポートされるようになりました (webmat で指摘されているように)。
concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1) # => "12"
そして、Ruby 1.9 では Proc とラムダがアリティで同じように動作するという、Michiel de Mare (OP) は間違っています。上記で指定したように、1.8 からの動作がまだ維持されていることを確認しました。
break
実際、ステートメントは Procs やラムダのどちらでもあまり意味がありません。Procs では、ブレークはすでに完了している Proc.new から戻ります。また、ラムダは本質的にメソッドであり、メソッドのトップレベルから中断することは決してないため、ラムダから中断することは意味がありません。
next
, redo
, 、 そして raise
Proc とラムダの両方で同じように動作します。一方 retry
どちらでも許可されていないため、例外が発生します。
そして最後に、 proc
このメソッドは一貫性がなく、予期しない動作をするため、決して使用しないでください。Ruby 1.8 では実際にラムダを返します。Ruby 1.9 ではこの問題は修正され、Proc を返すようになりました。Proc を作成したい場合は、そのままにしてください Proc.new
.
詳細については、O'Reilly's を強くお勧めします。 Ruby プログラミング言語 これが私の情報源です。
見つけました このページ これは何が違うのかを示しています Proc.new
そして lambda
は。このページによると、唯一の違いは、ラムダは受け入れる引数の数が厳密であるのに対し、ラムダは受け入れる引数の数が厳密であることです。 Proc.new
欠落している引数を次のように変換します nil
. 。違いを示す IRB セッションの例を次に示します。
irb(main):001:0> l = lambda { |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: wrong number of arguments (1 for 2) from (irb):1 from (irb):5:in `call' from (irb):5 from :0 irb(main):006:0> p.call "hello" TypeError: can't convert nil into String from (irb):2:in `+' from (irb):2 from (irb):6:in `call' from (irb):6 from :0
このページでは、エラー耐性のある動作を特に必要としない限り、lambda を使用することも推奨しています。私もこの意見に同意します。ラムダを使用する方がもう少し簡潔であるように思えますが、違いがこれほどわずかであるため、平均的な状況ではラムダを使用する方が良い選択であると思われます。
Ruby 1.9 については、申し訳ありませんが、1.9 についてはまだ調べていませんが、それほど大きく変更されるとは思えません (ただし、私の言葉を鵜呑みにしないでください。いくつかの変更については聞いているようですので、おそらくそこは間違っています)。
Proc は古いものですが、return のセマンティクスは (少なくとも私が言語を学習していた頃は) 私にとって非常に直観に反するものでした。理由は次のとおりです。
- proc を使用している場合は、何らかの関数パラダイムを使用している可能性が高くなります。
- Proc は、囲んでいるスコープの外に戻ることができます (前の回答を参照)。これは基本的に goto であり、本質的には非常に非機能的です。
Lambda は機能的に安全で、推論が簡単です。私は常に proc の代わりに Lambda を使用しています。
微妙な違いについてはあまり言えません。ただし、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 はそれ自体とそれを呼び出した関数から戻ります。
明確ではないのは、それぞれを使用する理由です。lambda は、関数型プログラミングの意味で物事が行うべきことを期待しているものです。これは基本的に、現在のスコープが自動的にバインドされる匿名メソッドです。2 つのうち、おそらく lambda を使用する必要があります。
一方、Proc は言語自体を実装するのに非常に役立ちます。たとえば、「if」ステートメントや「for」ループを実装できます。プロシージャ内で見つかった戻り値はすべて、単なる「if」ステートメントではなく、それを呼び出したメソッドから返されます。これが言語の仕組みであり、「if」ステートメントの仕組みであるため、私の推測では、Ruby はこれを秘密裏に使用しており、強力であると思われたために公開しただけであると考えられます。
これが実際に必要になるのは、ループや if-else 構造などの新しい言語構造を作成する場合のみです。
それを確認する良い方法は、ラムダが独自のスコープで (メソッド呼び出しであるかのように) 実行されるのに対し、Proc は呼び出しメソッドとインラインで実行されるように見えることです。少なくとも、どちらを使用するかを決定する良い方法です。いずれの場合にも。
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
Rubyのクロージャ は、Ruby でブロック、ラムダ、プロシージャがどのように動作するかを Ruby で概説したものです。
lambda は他の言語と同様に期待どおりに機能します。
有線の Proc.new
驚きと混乱を招く。
の return
によって作成されたプロシージャ内のステートメント 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
, 、これは(間違った)引数の処理です。ラムダはそれについて不平を言いますが、 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
, 、本当に混乱します。
Accordion Guy の反応について詳しく説明すると、次のようになります。
注目してください Proc.new
ブロックを渡されて proc out を作成します。私はそれを信じています lambda {...}
は、ブロックを渡すメソッド呼び出しではなく、一種のリテラルとして解析されます。 return
メソッド呼び出しにアタッチされたブロック内からの呼び出しは、ブロックではなくメソッドから返されます。 Proc.new
ケースはその一例です。
(これは1.8です。これが 1.9 にどのように変換されるかはわかりません。)
少し遅れましたが、素晴らしいことですが、あまり知られていないことが 1 つあります。 Proc.new
コメントではまったく言及されていません。のように ドキュメンテーション:
Proc::new
ブロックが付加されたメソッド内でのみ、ブロックなしで呼び出すことができます。その場合、 ブロックはに変換されますProc
物体。
そうは言っても、 Proc.new
生成メソッドを連鎖させてみましょう:
def m1
yield 'Finally!' if block_given?
end
def m2
m1 &Proc.new
end
m2 { |e| puts e }
#⇒ Finally!
との行動の違い return
私見ですが、この 2 つの最も重要な違いは次のとおりです。また、ラムダは Proc.new よりも入力が少ないため、ラムダの方が好きです :-)
それは強調する価値がある return
proc では、字句的に囲んでいるメソッドから戻ります。つまり、 プロシージャが作成されたメソッド, ない procを呼び出したメソッド。これは、proc の Closure プロパティの結果です。したがって、次のコードは何も出力しません。
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
は、その字句コンテキストで実行されるブロックでは問題ありませんが、別のコンテキストで実行されるプロシージャで使用すると直感的ではありません。