私の知る限り、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にして、ブロックの暗黙的な配列分解に依存するだけです。Hash
Array
[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 とラムダの両方の存在を受け入れることも小さなステップにすぎません。これらはそれぞれブロックとメソッドのように動作するからです。
- procs
return
を囲んでいるメソッドから (ブロックと同じように)、ブロックが行うのとまったく同じように引数をバインドします。
- それ自体からのラムダ
return
(メソッドと同様) であり、メソッドとまったく同じように引数をバインドします。
IOW: プロシージャ/ラムダの二分法は、ブロック/メソッドの二分法を反映しているだけです。
実際には、考慮すべきケースがかなり多いことに注意してください。例えば とはself
どういう意味ですか? ということですか
self
ブロックが書き込まれた時点で何があったか
self
ブロックが実行される時点で何が何でも
- ブロック自体
そしてどうreturn
ですか?ということですか
- ブロックが書かれているメソッドから戻る
- ブロックが実行されるメソッドから戻る
- ブロック自体から戻る?
パラメータ バインディングの Ruby 固有の特性を考慮しなくても、これで既に9 つの可能性が得られます。
さて、カプセル化の理由から、上記の #2 は非常に悪いアイデアです。
いつものように、それは言語設計者の好みの問題です。Ruby には他にもそのような冗長性があります。なぜインスタンス変数とローカル変数の両方が必要なのですか? レキシカル スコープがオブジェクトの場合、ローカル変数はレキシカル スコープのインスタンス変数にすぎず、ローカル変数は必要ありません。そして、なぜインスタンス変数とメソッドの両方が必要なのでしょうか? そのうちの 1 つで十分です。メソッドの getter/setter ペアはインスタンス変数を置き換えることができ (そのような言語の例については Newspeak を参照)、インスタンス変数に割り当てられたファースト クラスのプロシージャはメソッドを置き換えることができます (Self、Python、JavaScript を参照)。なぜクラスとモジュールの両方が必要なのですか? クラスの混在を許可すると、モジュールを取り除き、クラスをクラスとミックスインの両方として使用できます。そして、なぜミックスインが必要なのですか? すべてがメソッド呼び出しの場合、いずれにせよ、クラスは自動的に mixin になります (例については、Newspeak を参照してください)。そしてもちろん、オブジェクト間の直接継承を許可する場合、クラスはまったく必要ありません (Self、Io、Ioke、Seph、JavaScript を参照してください)。