2016-10-25 1 views
2

Il y a le cas de la chaîne recenseur je ne peux pas obtenir ma tête autour:chaîne recenseur commençant par trouver

[1, 2, 3, 4, 5].find.map { |x| x * x } 
#=> [1, 4, 9, 16, 25] 

Ce retourne le tableau de carrés de valeur initiale, mais j'attendre pour revenir juste le [1]

J'ai essayé de tout déconstruire et c'est ce que j'ai réalisé: .map est appelé sur un énumérateur find pour le tableau original. Il appelle each sur lui-même pour obtenir des valeurs pour l'itération. each sur l'énumérateur délègue l'itération à la méthode pour laquelle l'énumérateur a été créé, c'est-à-dire find. find obtient le premier élément de tableau, le restitue, et il continue à être cédé jusqu'à ce qu'il atteigne le bloc dans l'exemple. La valeur est au carré, le bloc le renvoie, le bloc each sous-jacent dans map renvoie la définition [1], il descend au find, et puisqu'il est true dans un sens booléen, je m'attends à ce que find revienne à ce point, finissant effectivement l'itération, mais en quelque sorte, il continue à alimenter les valeurs du tableau jusqu'au bloc map.

Ce n'est pas un exemple du monde réel, j'essaie juste de comprendre comment lire ces chaînes correctement, et cette affaire m'a dérouté.

UPD

Comme il a été suggéré à plusieurs reprises que find étant appelé sans de défaillance »retourne bloc recenseur, voici un exemple:

[1, 2, 3, 4, 5].find 
#=> #<Enumerator: [1, 2, 3, 4, 5]:find> 

[1, 2, 3, 4, 5].find.each { |x| x < 4 } 
#=> 1 
+0

Appel 'find' sans un bloc fait pas beaucoup de sens, il retourne [par défaut' EACH_ENUMERATOR' ] (https://ruby-doc.org/core-2.2.0/Enumerable.html#method-i-find) (cliquez sur "show source".) – mudasobwa

+0

Nitpicking, mais 'find' retournerait un seul élément ('1'), pas un tableau (' [1] '). – Stefan

+0

@mudasobwa '[1,2,3,4,5] .find' renvoie' # ', et il y a juste' RETURN_ENUMERATOR' dans la source. Peut-être que je ne regarde pas dans la bonne direction? Mais oui, cela n'a aucun sens, c'est juste un exemple synthétique pour illustrer un comportement étrange. – Roman

Répondre

0

Ok, j'ai enfin compris.

La raison find ne se termine pas l'itération après la première valeur est traitée par bloc, est que collect_i iterator au sein collect aka map méthode du module Enumerable retourne explicitement nil après chaque itération, peu importe quelle était la valeur de retour d'un bloc fourni avec un appel à map ou collect. Ici, il est, tiré de enum.c:

static VALUE 
collect_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary)) 
{ 
    rb_ary_push(ary, enum_yield(argc, argv)); 

    return Qnil; 
} 

donc appel interne à find sur le tableau initial obtient toujours nil à la suite de donner une valeur, et donc ne pas arrêter l'itération jusqu'à ce que le dernier élément est traité. Cela est facile à prouver en téléchargeant Ruby comme archive et modifier cette fonction comme ceci:

static VALUE 
collect_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary)) 
{ 
    rb_ary_push(ary, enum_yield(argc, argv)); 

    return ary; 
} 

Après avoir enregistré et la construction rubis de la source modifiée, nous obtenons ceci:

irb(main):001:0> [1,2,3,4,5].find.map { |x| x*x } => [1]

Une autre chose intéressante, est que Rubinius implémente collect exactement comme ça, donc je pense qu'il y a une chance que MRI et Rubinius produisent des résultats différents pour cette déclaration. Je n'ai pas d'installation Rubinius en ce moment, mais je vais vérifier cela quand j'aurai une oppoprtunity et que je mettrai à jour ce post avec le résultat.

Je ne sais pas si ce sera jamais d'aucune utilité à personne, sauf peut-être pour satisfaire sa curiosité, mais quand même :)