Перечисление Ruby и RETURN_ENUMERATOR - Вопросы, касающиеся внутренних компонентов Ruby's C - PullRequest
0 голосов
/ 07 июля 2010

Я немного озадачен тем, как Ruby управляет созданием перечислителей. Блочная итерация имеет смысл и работает для меня; Я до сих пор не понимаю, как возврат Enumerator должен функционировать в коде.

Вот код, с которым я работаю:

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; предположительно, то же самое работает в случае счетчика, но как? Кажется, что каждый раз через цикл он возвращает Enumerator, так как же Enumerator узнает, что нужно вернуть соответствующий соответствующий объект? rb_yield получает rb_frame_hash в качестве переданного аргумента, но RETURN_ENUMERATOR, кажется, принимает аргументы, которые передаются методу, когда перечислитель вызывает метод внутри. Очевидно, что перечислитель вызывает сам метод - возможно, с каким-то внутренним блоком, который просто возвращает экземпляр rb_frame_hash?

Любое понимание внутренних органов приветствуется.

-Asher

1 Ответ

0 голосов
/ 08 июля 2010

Чтобы попытаться ответить на мой собственный вопрос:

Когда вызывается RETURN_ENUMERATOR, вызывается rb_enumeratorize, который создает перечислитель.Перечислитель возвращается;когда: next вызывается на перечислителе, волокно инициализируется (при необходимости) или возобновляется.Каждый раз, когда вызывается next, Fiber выполняет итерацию внутреннего блока один раз для получения следующего элемента итератора (установка no_next в структуре C перечислителя и вызов rb_fiber_yield в волокне перечислителя).

Так что может показаться, что активность цикла не обязательно должна выполняться перед RETURN_ENUMERATOR.Я еще не ясно, какие действия после перечисления в функции, возвращающей перечислитель, в случае, если блок не был предоставлен.

...