Yann :
Puisqu’on parle de yield, est-ce qu’on peut dire que “yield
permet de passer à une fonction anonyme à une méthode” ?
Si non, qu’est-ce qui serait une bonne explication en français ?
Thibaut :
yield permet d’invoquer le bloc (la fonction anonyme) passé à la
méthode courante ?
Guillaume :
On peut dire que yield exécute la fonction anonyme passée
à la méthode dans laquelle yield est appelé.
Vous sous-entendez tous qu’un block (bloc de code) est
exactement la même chose qu’une fonction anonyme.
Guillaume dans un autre message considère qu’une fonction
anonyme est un objet Proc.
Alors là , je suis pas vraiment d’accord et quitte à passer pour
un chipoteur, je dis non !
Si on considère qu’une fonction anonyme est un objet
Proc, alors un block n’est certainement pas une fonction anonyme,
et même un block n’est pas forcément un Proc.
Cela ne signifie pas que block et Proc peuvent être intimement
liés. La preuve, c’est qu’on ne peut instancier de Proc
sans block.
Un block peut être converti en Proc (et l’inverse) sans qu’on
s’en rende compte (ou qu’on fasse attention).
Bon, comme j’aime à le rappeler, tout n’est pas objet en Ruby,
mais presque tout.
En l’occurrence, les Procs sont des objets mais les blocks ne
le sont pas (ce qui est une différence considérable, convenons-en).
Une autre différence est la facilité d’utilisation. Un Proc est un
objet,
donc on peut le passer facilement en argument d’une méthode,
le refiler à gauche à droite, faire référencer une nouvelle variable
dessus, etc.
Un bloc de code est associé ou attaché à l’invocation d’une méthode :
foo(un, deux, trois) { |e| … }
Là , mon bloc est lié à la méthode foo et je ne peux l’attacher
ailleurs qu’en rusant un peu.
Il faut que j’ouvre auparavant une petite parenthèse sur la
“déclaration” (mmh je trouve pas de meilleur mot)
explicite ou implicite d’un bloc de code.
manière implicite :
def foo(un, deux)
…
eventuellement un yield qq part dans le code
end
(ce qui est intéressant, c’est qu’on peut très bien associer
un block à une invocation de méthode sans que ladite méthode
n’en fasse quoi que ce soit, ie n’utilise pas yield. Le block ne
sert évidemment à rien, mais c’est un mécanisme intéressant,
auquel je reviendrai un peu plus tard peut-être)
Zifro nous a décrit cette méthode, mais pas la suivante.
explicite :
def foo(un, deux, &blk)
…
utilisation de yield ou blk.call(…)
end
utilisation identique
foo(‘ruby’, ‘rails’) { |m| puts m }
Ici, Ruby prend le block, le convertit en Proc et le passe en argument
de foo en tant que paramètre blk. Donc, dans le corps de foo, on
peut utiliser blk comme un Proc et faire un blk.call() au lieu d’un
yield.
Comme j’ai désormais un objet et non plus un block qui n’est pas
un objet, je peux le refourguer lors d’un sous-appel à une
autre méthode ou encore le stocker dans une variable d’instance
(pour utilisation ultérieure avec #call).
Le & devant le blk dans la signature de foo finalement indique
qu’il y a une conversion en Proc à faire.
Dans d’autre situation le & permet la conversion dans l’autre sens,
d’un Proc à un block. Ainsi cet exemple :
def foo(un, deux)
yield
end
foo(‘ruby’, ‘rails’) { puts “hello” }
p = lambda { puts “bonjour” }
foo(‘ruby’, ‘rails’, &p)
p est un Proc (créé à partir d’un… bloc de code
si on rajoute ce bout (et non bloc :)) de code :
def bar(un, deux, &blk)
blk est un Proc
foo(un, deux, &blk)
end
bar(‘ruby’, ‘rails’) { puts “hello” }
Là j’ai pu “refiler” le bloc de code à foo, alors qu’il
était au départ attaché à mon appel de bar.
autre exemple, garder mon block au chaud (converti en
Proc) dans une variable d’instance :
class A
def foo(un, deux, &blk)
@bar = blk
end
end
Après, je voulais parler de quoi…
Je l’ai dit déjà ? Quand on utilise la manière implicite,
def foo(un, deux)
yield
end
Le bloc de code n’est pas automatiquement converti
en Proc. Il n’y a pas de création d’objet Proc.
Je voulais dire sûrement autre chose mais je ne sais
plus. Un objet Proc, c’est à mon avis la même chose
qu’une fermeture (closure). Une closure est une
fonction (en Ruby, méthode) anonyme. Une fonction
anonyme n’est pas forcément une closure.
– Jean-François.