Fonction anonyme (etait: Petites questions sur l'utilisation

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 :slight_smile:

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.

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.

Non, si je me suis mal exprimé, j’en suis navré.
Je voulais dire qu’on peut stocker une fonction anonyme en la
convertissant en objet Proc, pour la sauvegarder dans une variable par
exemple.

Alors là , je suis pas vraiment d’accord et quitte à passer pour
un chipoteur, je dis non !

Aucun problème, on est là pour discuter :slight_smile:
[ je ne reprends pas toute la suite, puisqu’on d’accord ]

Un block peut être converti en Proc (et l’inverse) sans qu’on
s’en rende compte (ou qu’on fasse attention).

Oui, tu le montres plus loin dans le message.

Bon, comme j’aime à le rappeler, tout n’est pas objet en Ruby,
mais presque tout.

Oui, on en avait déjà débattu, ici ou sur ruby-fr, et sur #rubyfr
également.

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).

Oui, pour des raisons de performances, et quelqu’un a déjà eu la
gentillesse
de faire un comparatif : Is a block converted to a Proc object before yield? - Ruby - Ruby-Forum

ailleurs qu’en rusant un peu.
end

def foo(un, deux, &blk)

utilisation de yield ou blk.call(…)

end

Oui, et à noter que l’on peut, ou pas, fournir le bloc, il se comporte
comme &blk=nil.

autre méthode ou encore le stocker dans une variable d’instance
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 :slight_smile:

C’est à ma connaissance le seul moyen pour créer un objet Proc.
A moins qu’on ne puisse en obtenir un à partir d’un objet Method ou
UnboundMethod ?

é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

Je pense que l’on a tous bien compris la différence : avec un Proc, on
manipule
plus facilement le code passé à la méthode, quitte à transformer un
block en Proc
pour lui assurer une persistence au sein du programme, sauvegardé dans
une variable
ou pour le passer à une autre méthode.

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.

Pour autant que je sache, il n’est pas converti du tout en 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.

Peux-tu clarifier s’il te plaît ?
Je pense qu’il n’y a pas qu’à moi que ce dernier paragraphe paraît
obscur…
Pour moi il y a les fonctions anonymes, telle { |elem| puts elem },
que l’on peut
convertir en objet Proc, pour être manipulées plus facilement, stockée,
etc…

Merci en tout cas pour tout ce récapitulatif (exhaustif me risquerais-je
à dire)
de l’utilisation des blocks, des Proc, de call et de yield.
Enfin, pour les curieux, voir
http://innig.net/software/ruby/closures-in-ruby.rb, un
programme “pédagogique” à exécuter et lire en même temps, sur tout ce
dont
on a parlé ici.

Cordialement,