0

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のインスタンスを返すだけのある種の内部ブロックを使用しているのでしょうか。

内部への洞察は大歓迎です。

-アッシャー

4

1 に答える 1

0

私自身の質問に答えようとするには:

RETURN_ENUMERATORが呼び出されると、rb_enumeratorizeが呼び出され、列挙子が作成されます。列挙子が返されます。列挙子で:nextが呼び出されると、ファイバーは初期化(必要な場合)または再開されます。:nextが呼び出されるたびに、ファイバーは内部で提供されたブロックを1回繰り返して、次のイテレーター項目を取得します(列挙子のC構造体でno_nextを設定し、列挙子のファイバーでrb_fiber_yieldを呼び出します)。

したがって、ループアクティビティはRETURN_ENUMERATORの前に実行する必要はないように思われます。ブロックが提供されなかった場合に列挙子を返す関数で列挙した後のアクションについては、まだ明確ではありません。

于 2010-07-08T08:56:37.260 に答える