2010-07-07 3 views
0

Je suis un peu confus sur la façon dont Ruby gère la création des Enumérateurs. L'itération par bloc a du sens et fonctionne pour moi; Je suis encore confus comment le retour d'un énumérateur est censé fonctionner en code.Ruby Enumeration et RETURN_ENUMERATOR - Questions concernant les Internes C de Ruby

Voici le code, je travaille avec:

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; 
} 

Comment la dernière ligne dans la boucle while être appelée dans le cas d'un recenseur?

Est-ce que toute mon activité de boucle doit avoir lieu avant les appels à RETURN_ENUMERATOR (puisque RETURN_ENUMERATOR doit probablement précéder rb_yield())? Et si je veux que quelque chose se produise une fois l'itération interne terminée? Avec le bloc je peux simplement le mettre après la boucle while; probablement les mêmes travaux dans le cas d'un énumérateur - mais comment? Il semble que chaque fois que la boucle retourne un énumérateur, comment l'énumérateur sait-il renvoyer l'objet correspondant? rb_yield obtient rb_frame_hash comme argument passé, mais RETURN_ENUMERATOR semble prendre les arguments qui sont relayés à la méthode lorsque l'énumérateur appelle la méthode en interne. Il est donc clair que l'Enumérateur appelle la méthode elle-même - peut-être avec un type de bloc interne qui renvoie simplement l'instance de rb_frame_hash?

Tout aperçu des composants internes est apprécié.

-Asher

Répondre

0

Pour tenter de répondre à ma propre question:

Lorsque RETURN_ENUMERATOR est appelé, rb_enumeratorize est appelée, ce qui crée un recenseur. L'énumérateur est renvoyé. quand: next est appelée sur l'énumérateur, une fibre est initialisée (si nécessaire) ou reprise. Chaque fois que: next est appelé, la fibre itère un bloc fourni en interne une fois afin d'obtenir l'élément d'itération suivant (définissant no_next dans la structure C de l'énumérateur et appelant rb_fiber_yield sur la fibre de l'énumérateur). Il semblerait donc que l'activité de boucle ne doit pas avoir lieu avant RETURN_ENUMERATOR.

Je ne suis pas encore clair sur les actions après l'énumération dans la fonction renvoyant un énumérateur dans le cas où un bloc n'a pas été fourni.