Rubyが列挙子の作成をどのように処理するかについて少し混乱しています。ブロックベースの反復は理にかなっており、私にとってはうまく機能しています。列挙子の戻りがコード的にどのように機能するのか、私はまだ混乱しています。
これが私が使っているコードです:
VALUE rb_RPRuby_Sender_Kernel_each_backtrace_frame( int argc,
VALUE* args,
VALUE rb_self ) {
rb_thread_t* c_thread = GET_THREAD();
// Get the current frame - we're doing a backtrace, so our current working frame to start is the first previous thread
rb_control_frame_t* c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( RUBY_VM_PREVIOUS_CONTROL_FRAME( c_thread->cfp ) );
// c_top_of_control_frame describes the top edge of the stack trace
// set c_top_of_control_frame to the first frame in <main>
rb_control_frame_t* c_top_of_control_frame = RUBY_VM_NEXT_CONTROL_FRAME( RUBY_VM_NEXT_CONTROL_FRAME( (void *)( c_thread->stack + c_thread->stack_size ) ) );
// for each control frame:
while ( c_current_context_frame < c_top_of_control_frame ) {
VALUE rb_frame_hash = rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( & c_current_context_frame );
// if we don't have a block, return enumerator
RETURN_ENUMERATOR( rb_self, 0, NULL );
// otherwise, yield the block
rb_yield( rb_frame_hash );
c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_current_context_frame );
}
return Qnil;
}
列挙子の場合、whileループの最後の行はどのように呼び出されますか?
すべてのループアクティビティは、RETURN_ENUMERATORを呼び出す前に実行する必要がありますか(RETURN_ENUMERATORはおそらくrb_yield()の前に実行する必要があるため)?
内部反復が終了したら何かを起こさせたい場合はどうすればよいですか?ブロックを使用すると、whileループの後に簡単に配置できます。おそらく、列挙子の場合も同じように機能しますが、どのようにしたらよいでしょうか。ループを通過するたびに列挙子が返されるように見えますが、列挙子は適切な対応するオブジェクトを返すことをどのようにして知るのでしょうか。rb_yieldは、渡されたargとしてrb_frame_hashを取得しますが、RETURN_ENUMERATORは、列挙子がメソッドを内部的に呼び出すときに、メソッドに中継される引数を取得するようです。つまり、列挙子がメソッド自体を呼び出していることは明らかです。おそらく、rb_frame_hashのインスタンスを返すだけのある種の内部ブロックを使用しているのでしょうか。
内部への洞察は大歓迎です。
-アッシャー