私は、列挙子をジェネレーターとして使用する方法を説明するリソースを読んでいます。たとえば、次のようになります。
triangular_numbers = Enumerator.new do |yielder|
number = 0
count = 1
loop do
number += count
count += 1
yielder.yield number
end
end
print triangular_numbers.next, " "
print triangular_numbers.next, " "
print triangular_numbers.next, " "
ここの目的yielder
、それが取る値、そしてこのコードがプログラムの残りのコードと並行してどのように実行されるのか理解できません。
実行は上から始まり、おそらくブロックがコードに値を「渡す」ときに一時停止します。
コンパイラの視点からこれらすべてがどのように実行されるのかを誰か説明してもらえますか?
ベストアンサー1
あなたにとって興味深いものを見つけたと思います。
この記事:「Ruby 2.0 は一生懸命働くので、あなたは怠け者になれます」Pat Shaughnessy 著Eager 評価と Lazy 評価の背後にある考え方を説明し、それが Enumerale、Generator、Yielder などの「フレームワーク クラス」とどのように関係するかについても説明します。主に LazyEvaluation を実現する方法の説明に重点を置いていますが、それでもかなり詳細です。
元のソース:「Ruby 2.0 は一生懸命働くので、あなたは怠け者になれます」Pat Shaughnessy 著
Ruby 2.0 では、Enumerator::Lazy というオブジェクトを使用して遅延評価を実装しています。これが特別なのは、両方の役割を果たすことです。これは列挙子であり、一連の Enumerable メソッドも含んでいます。列挙ソースからデータを取得するために各メソッドを呼び出し、列挙の残りの部分にデータを渡します。Enumerator::Lazy は両方の役割を果たすため、これらを連鎖させて 1 つの列挙を生成することができます。
これが Ruby の遅延評価の鍵です。データ ソースからの各値は my ブロックに渡され、その結果はすぐに列挙チェーンに沿って渡されます。この列挙は先行評価ではありません。Enumerator::Lazy#collect メソッドは値を配列に収集しません。代わりに、各値は繰り返しの yield によって Enumerator::Lazy オブジェクトのチェーンに沿って 1 つずつ渡されます。 collect やその他の Enumerator::Lazy メソッドへの一連の呼び出しを連鎖させた場合、各値は、ブロックの 1 つから次のブロックへと 1 つずつ連鎖に沿って渡されます。Enumerable
#first は、遅延列挙子に対してそれぞれを呼び出すことで反復処理を開始し、十分な値がある場合に例外を発生させることで反復処理を終了します。
結局のところ、これが遅延評価の背後にある重要な考え方です。計算チェーンの最後の関数またはメソッドが実行プロセスを開始し、プログラムのフローは、必要なデータ入力だけを取得するまで、関数呼び出しのチェーンを逆方向に処理します。Ruby は、Enumerator::Lazy オブジェクトのチェーンを使用してこれを実現します。