J'essaie de comprendre les blocs yield
et comment ils fonctionnent dans Ruby.
Comment est-il yield
utilisé? Beaucoup d'applications Rails que j'ai examinées sont utilisées yield
de manière étrange.
Quelqu'un peut-il m'expliquer ou me montrer où aller pour les comprendre?
Réponses:
Oui, c'est un peu déroutant au début.
Dans Ruby, les méthodes peuvent recevoir un bloc de code afin d'effectuer des segments de code arbitraires.
Lorsqu'une méthode attend un bloc, elle l'invoque en appelant la
yield
fonction.C'est très pratique, par exemple, pour parcourir une liste ou pour fournir un algorithme personnalisé.
Prenons l'exemple suivant:
Je vais définir une
Person
classe initialisée avec un nom et fournir unedo_with_name
méthode qui, lorsqu'elle est invoquée, passerait simplement l'name
attribut au bloc reçu.Cela nous permettrait d'appeler cette méthode et de passer un bloc de code arbitraire.
Par exemple, pour imprimer le nom, nous ferions:
Imprime:
Remarquez, le bloc reçoit, en paramètre, une variable appelée
name
(NB vous pouvez appeler cette variable comme bon vous semble, mais il est logique de l'appelername
). Lorsque le code invoque,yield
il remplit ce paramètre avec la valeur de@name
.Nous pourrions fournir un autre bloc pour effectuer une action différente. Par exemple, inversez le nom:
Nous avons utilisé exactement la même méthode (
do_with_name
) - c'est juste un bloc différent.Cet exemple est trivial. Les utilisations les plus intéressantes sont de filtrer tous les éléments d'un tableau:
Ou, nous pouvons également fournir un algorithme de tri personnalisé, par exemple basé sur la taille de la chaîne:
J'espère que cela vous aide à mieux le comprendre.
BTW, si le bloc est facultatif, vous devez l'appeler comme:
Si n'est pas facultatif, il suffit de l'invoquer.
ÉDITER
@hmak a créé un repl.it pour ces exemples: https://repl.it/@makstaks/blocksandyieldsrubyexample
la source
racsO
sithe_name = ""
"Oscar"
(n'est pas très clair dans la réponse)person.do_with_name {|string| yield string, something_else }
Dans Ruby, les méthodes peuvent vérifier si elles ont été appelées de telle manière qu'un bloc a été fourni en plus des arguments normaux. En règle générale, cela se fait à l'aide de la
block_given?
méthode, mais vous pouvez également faire référence au bloc en tant que Proc explicite en préfixant une esperluette (&
) avant le nom de l'argument final.Si une méthode est invoquée avec un bloc, la méthode peut
yield
contrôler le bloc (appeler le bloc) avec quelques arguments, si nécessaire. Considérez cet exemple de méthode qui illustre:Ou, en utilisant la syntaxe spéciale des arguments de bloc:
la source
Il est tout à fait possible que quelqu'un fournisse une réponse vraiment détaillée ici, mais j'ai toujours trouvé que cet article de Robert Sosinski était une excellente explication des subtilités entre les blocs, les procs et les lambdas.
Je dois ajouter que je pense que le post auquel je fais un lien est spécifique à ruby 1.8. Certaines choses ont changé dans ruby 1.9, telles que les variables de bloc étant locales au bloc. En 1.8, vous obtiendrez quelque chose comme ceci:
Alors que 1.9 vous donnerait:
Je n'ai pas 1.9 sur cette machine, donc ce qui précède pourrait avoir une erreur.
la source
Je voulais en quelque sorte ajouter pourquoi vous feriez les choses de cette façon aux réponses déjà excellentes.
Je ne sais pas de quelle langue vous venez, mais en supposant qu'il s'agit d'un langage statique, ce genre de chose vous semblera familier. Voici comment lire un fichier en java
Ignorant tout le chaînage de flux, l'idée est la suivante
Voici comment vous le faites en rubis
Extrêmement différent. Briser celui-ci
Ici, au lieu de gérer les étapes un et deux, vous déléguez essentiellement cela dans une autre classe. Comme vous pouvez le voir, cela réduit considérablement la quantité de code que vous devez écrire, ce qui facilite la lecture et réduit les risques de fuites de mémoire ou de verrous de fichiers non effacés.
Maintenant, ce n'est pas comme si vous ne pouviez pas faire quelque chose de similaire en java, en fait, les gens le font depuis des décennies maintenant. Cela s'appelle le modèle de stratégie . La différence est que sans blocs, pour quelque chose de simple comme l'exemple de fichier, la stratégie devient excessive en raison de la quantité de classes et de méthodes que vous devez écrire. Avec les blocs, c'est une manière si simple et élégante de le faire, que cela n'a aucun sens de NE PAS structurer votre code de cette façon.
Ce n'est pas la seule façon dont les blocs sont utilisés, mais les autres (comme le modèle Builder, que vous pouvez voir dans l'api form_for dans les rails) sont suffisamment similaires pour qu'il soit évident de savoir ce qui se passe une fois que vous avez tourné la tête autour de cela. Lorsque vous voyez des blocs, il est généralement sûr de supposer que l'appel de méthode est ce que vous voulez faire, et le bloc décrit comment vous voulez le faire.
la source
File.readlines("readfile.rb").each_with_index do |line, index| puts "#{index + 1}: #{line}" end
et rions encore plus des gars Java.IO.foreach('readfile.rb').each_with_index { |line, index| puts "#{index}: #{line}" }
(plus aucun problème de mémoire)J'ai trouvé cet article très utile. En particulier, l'exemple suivant:
qui devrait donner la sortie suivante:
Donc, essentiellement, à chaque appel à
yield
ruby, le code sera exécuté dans ledo
bloc ou à l'intérieur{}
. Si un paramètre est fourni àyield
alors il sera fourni comme paramètre à lado
bloc.Pour moi, c'était la première fois que je comprenais vraiment ce que
do
faisaient blocs. C'est fondamentalement un moyen pour la fonction de donner accès aux structures de données internes, que ce soit pour l'itération ou pour la configuration de la fonction.Donc, dans les rails, vous écrivez ce qui suit:
Cela exécutera la
respond_to
fonction qui produit ledo
bloc avec leformat
paramètre (interne) . Vous appelez ensuite la.html
fonction sur cette variable interne qui à son tour donne le bloc de code pour exécuter larender
commande. Notez que.html
cela ne donnera que si c'est le format de fichier demandé. (technicité: ces fonctions n'utilisentblock.call
pas réellementyield
comme vous pouvez le voir à la source mais la fonctionnalité est essentiellement la même, voir cette question pour une discussion.) Cela permet à la fonction d'effectuer une initialisation puis de saisir le code appelant et puis poursuivez le traitement si nécessaire.Ou, autrement dit, c'est semblable à une fonction prenant une fonction anonyme comme argument et l'appelle ensuite en javascript.
la source
Dans Ruby, un bloc est essentiellement un morceau de code qui peut être transmis et exécuté par n'importe quelle méthode. Les blocs sont toujours utilisés avec des méthodes qui leur fournissent généralement des données (sous forme d'arguments).
Les blocs sont largement utilisés dans les gemmes Ruby (y compris Rails) et dans le code Ruby bien écrit. Ce ne sont pas des objets et ne peuvent donc pas être affectés à des variables.
Syntaxe de base
Un bloc est un morceau de code entouré par {} ou do..end. Par convention, la syntaxe d'accolade doit être utilisée pour les blocs à une seule ligne et la syntaxe do..end doit être utilisée pour les blocs à plusieurs lignes.
Toute méthode peut recevoir un bloc comme argument implicite. Un bloc est exécuté par l'instruction yield dans une méthode. La syntaxe de base est:
Lorsque l'instruction yield est atteinte, la méthode meditate cède le contrôle au bloc, le code dans le bloc est exécuté et le contrôle est retourné à la méthode, qui reprend l'exécution immédiatement après l'instruction yield.
Lorsqu'une méthode contient une déclaration de rendement, elle s'attend à recevoir un bloc au moment de l'appel. Si aucun bloc n'est fourni, une exception sera levée une fois la déclaration de rendement atteinte. Nous pouvons rendre le bloc facultatif et éviter qu'une exception ne soit déclenchée:
Il n'est pas possible de passer plusieurs blocs à une méthode. Chaque méthode ne peut recevoir qu'un seul bloc.
Voir plus à: http://www.zenruby.info/2016/04/introduction-to-blocks-in-ruby.html
la source
J'utilise parfois "yield" comme ceci:
la source
Logger
ne doit pas effectuer de tâche si l'utilisateur n'en a pas besoin. Vous devriez cependant expliquer les vôtres ...Les rendements, pour le dire simplement, permettent à la méthode que vous créez de prendre et d'appeler des blocs. Le mot-clé yield est spécifiquement l'endroit où les «trucs» du bloc seront exécutés.
la source
Il y a deux points que je veux faire valoir au sujet du rendement ici. Tout d'abord, alors que de nombreuses réponses parlent ici de différentes façons de passer un bloc à une méthode qui utilise le rendement, parlons également du flux de contrôle. Ceci est particulièrement pertinent car vous pouvez générer plusieurs fois un bloc. Prenons un exemple:
Lorsque la méthode each est invoquée, elle s'exécute ligne par ligne. Maintenant, lorsque nous arrivons au bloc 3.times, ce bloc sera invoqué 3 fois. Chaque fois, il invoque le rendement. Ce rendement est lié au bloc associé à la méthode qui a appelé chaque méthode. Il est important de noter que chaque fois que yield est invoqué, il renvoie le contrôle au bloc de chaque méthode dans le code client. Une fois l'exécution du bloc terminée, il revient au bloc 3 fois. Et cela se produit 3 fois. Donc, ce bloc dans le code client est invoqué à 3 reprises distinctes puisque le rendement est explicitement appelé 3 fois distinctes.
Mon deuxième point concerne l'énumération et le rendement. enum_for instancie la classe Enumerator et cet objet Enumerator répond également à yield.
Notez donc que chaque fois que nous invoquons des types avec l'itérateur externe, il n'appellera le rendement qu'une seule fois. La prochaine fois que nous l'appellerons, il invoquera le prochain rendement et ainsi de suite.
Il y a une friandise intéressante en ce qui concerne enum_for. La documentation en ligne indique ce qui suit:
Si vous ne spécifiez pas de symbole comme argument pour enum_for, ruby raccordera l'énumérateur à chaque méthode du récepteur. Certaines classes n'ont pas de méthode each, comme la classe String.
Ainsi, dans le cas de certains objets invoqués avec enum_for, vous devez être explicite quant à votre méthode d'énumération.
la source
Le rendement peut être utilisé comme bloc sans nom pour renvoyer une valeur dans la méthode. Considérez le code suivant:
Vous pouvez créer une méthode "Up" à laquelle est affecté un argument. Vous pouvez maintenant affecter cet argument à yield qui appellera et exécutera un bloc associé. Vous pouvez affecter le bloc après la liste des paramètres.
Lorsque la méthode Up appelle yield, avec un argument, elle est transmise à la variable de bloc pour traiter la demande.
la source