13

私の知る限り、Ruby には基本的に 3 種類のクロージャがあります。メソッド、プロシージャ、ラムダ。それらの間に違いがあることは知っていますが、考えられるすべてのユースケースに対応する 1 つのタイプを持つことから逃れることはできませんか?

メソッドは procs や lambdas のように を呼び出すことself.method(method_name)で既に受け渡すことができます.procs と lambdas の間で私が認識している唯一の重要な違いは、lambdas は arity をチェックし、procs はreturn. では、それらをすべて 1 つにマージして、それで完了することはできませんか?

4

2 に答える 2

14

私の知る限り、Ruby には基本的に 3 種類のクロージャがあります。メソッド、プロシージャ、ラムダ。

いいえ、2 つあります。メソッドはクロージャではなく、proc とラムダだけです。(または、少なくともそうなる可能性がありますが、ほとんどはそうではありません。)

Ruby で再利用する実行可能コードの一部をパッケージ化するには、メソッドとブロックの 2 つの方法があります。厳密に言えば、ブロックは必要ありません。メソッドだけで十分です。しかし、ブロックは、概念的にも、意味的にも、構文的にも、非常に軽量であることを意図しています。それはメソッドには当てはまりません。

ブロックは軽量で使いやすいように設計されているため、引数がパラメーターにバインドされる方法など、いくつかの点でブロックの動作はメソッドとは異なります。ブロック パラメーターは、メソッド パラメーターよりも、割り当ての左側のようにバインドされます。

例:

単一の配列を複数のパラメーターに渡す:

def foo(a, b) end
foo([1, 2, 3]) # ArgumentError: wrong number of arguments (1 for 2)

a, b = [1, 2, 3]
# a == 1; b == 2

[[1, 2, 3]].each {|a, b| puts "a == #{a}; b == #{b}" }
# a == 1; b ==2

パラメータより少ない引数を渡す:

def foo(a, b, c) end
foo(1, 2) # ArgumentError

a, b, c = 1, 2
# a == 1; b == 2; c == nil

[[1, 2]].each {|a, b, c| puts "a == #{a}; b == #{b}; c == #{c}" }
# a == 1; b == 2; c == 

パラメータよりも多くの引数を渡す:

def foo(a, b) end
foo(1, 2, 3) # ArgumentError: wrong number of arguments (3 for 2)

a, b = 1, 2, 3
# a == 1; b == 2

[[1, 2, 3]].each {|a, b| puts "a == #{a}; b == #{b}" }
# a == 1; b == 2

[ちなみに、上記のブロックはクロージャではありません。]

これにより、たとえば、単一のEnumerable要素を常にブロックに生成してes で動作させるプロトコルが可能になります。単一の要素をofにして、ブロックの暗黙的な配列分解に依存するだけです。HashArray[key, value]

{one: 1, two: 2}.each {|k, v| puts "#{key} is assigned to #{value}" }

他の方法で書かなければならないものよりもはるかに理解しやすいです:

{one: 1, two: 2}.each {|el| puts "#{el.first} is assigned to #{el.last}" }

ブロックとメソッドのもう 1 つの違いは、メソッドはreturnキーワードを使用して値を返すのに対し、ブロックはnextキーワードを使用することです。

言語にメソッドとブロックの両方を含めることが理にかなっていることに同意する場合は、proc とラムダの両方の存在を受け入れることも小さなステップにすぎません。これらはそれぞれブロックとメソッドのように動作するからです。

  • procsreturnを囲んでいるメソッドから (ブロックと同じように)、ブロックが行うのとまったく同じように引数をバインドします。
  • それ自体からのラムダreturn(メソッドと同様) であり、メソッドとまったく同じように引数をバインドします。

IOW: プロシージャ/ラムダの二分法は、ブロック/メソッドの二分法を反映しているだけです。

実際には、考慮すべきケースがかなり多いことに注意してください。例えば ​​とはselfどういう意味ですか? ということですか

  • selfブロックが書き込まれた時点で何があったか
  • selfブロックが実行される時点で何が何でも
  • ブロック自体

そしてどうreturnですか?ということですか

  • ブロックが書かれているメソッドから戻る
  • ブロックが実行されるメソッドから戻る
  • ブロック自体から戻る?

パラメータ バインディングの Ruby 固有の特性を考慮しなくても、これで既に9 つの可能性が得られます。

さて、カプセル化の理由から、上記の #2 は非常に悪いアイデアです。

いつものように、それは言語設計者の好みの問題です。Ruby には他にもそのような冗長性があります。なぜインスタンス変数とローカル変数の両方が必要なのですか? レキシカル スコープがオブジェクトの場合、ローカル変数はレキシカル スコープのインスタンス変数にすぎず、ローカル変数は必要ありません。そして、なぜインスタンス変数とメソッドの両方が必要なのでしょうか? そのうちの 1 つで十分です。メソッドの getter/setter ペアはインスタンス変数を置き換えることができ (そのような言語の例については Newspeak を参照)、インスタンス変数に割り当てられたファースト クラスのプロシージャはメソッドを置き換えることができます (Self、Python、JavaScript を参照)。なぜクラスとモジュールの両方が必要なのですか? クラスの混在を許可すると、モジュールを取り除き、クラスをクラスとミックスインの両方として使用できます。そして、なぜミックスインが必要なのですか? すべてがメソッド呼び出しの場合、いずれにせよ、クラスは自動的に mixin になります (例については、Newspeak を参照してください)。そしてもちろん、オブジェクト間の直接継承を許可する場合、クラスはまったく必要ありません (Self、Io、Ioke、Seph、JavaScript を参照してください)。

于 2013-08-09T14:27:51.310 に答える
3

かなり良い説明http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/ですが、もう少し深く哲学的な説明が必要だと思います...

「しかし、考えられるすべてのユースケースに対応する 1 つのタイプを使用するだけで済むわけではありませんか?」に対する答えは、1 つだけで済むということだと思います。

それらが存在する理由は、Ruby が関数型とオブジェクト指向の両方のパラダイムからの式を使用して、開発者を可能な限り生産的にしようとしているからです。

于 2013-08-09T13:05:10.323 に答える