質問

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 のセマンティクスは (少なくとも私が言語を学習していた頃は) 私にとって非常に直観に反するものでした。理由は次のとおりです。

  1. proc を使用している場合は、何らかの関数パラダイムを使用している可能性が高くなります。
  2. 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 は、その字句コンテキストで実行されるブロックでは問題ありませんが、別のコンテキストで実行されるプロシージャで使用すると直感的ではありません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top