Dans cette séquence, je vous présente les blocks.
Les blocks sont des sortes de méthodes anonymes aussi
appelées des fermetures lexicales.
Elles sont partout dans Pharo.
Elles constituent la base pour les boucles, les
conditionnels, les itérateurs et même les traitements
graphiques, voire les langages dédiés.
En fait, elles sont au cœur du système, comme nous le verrons.
Pour la petite anecdote, elles ont seulement été
introduites dans la dernière version de Java.
Pour définir un block, on va utiliser en fait des crochets, syntaxiquement.
Ici, vous avez un exemple, on a un crochet ouvrant, un
ensemble d'expressions et puis un crochet fermant.
On va parler de définition d'un block.
Je vais vous prendre un autre exemple.
Ici, on a une expression : 1/0.
Si on exécute cette expression, on voit qu'elle retourne bien une erreur.
On encapsule cette expression dans un block, ici, je
définis un block qui contient l'expression 1/0.
Si j'évalue la définition de ce block, en fait, il ne se
passe rien, donc je n'ai pas d'erreur.
J'ai le block qui m'est retourné, parce qu'en fait la
définition d'un block n'exécute pas les expressions qu'il contient.
Donc ici, on peut définir autant de blocks qu'on veut,
les expressions à l'intérieur sont comme figées.
Pour évaluer les expressions qu'un block contient, il
faut le faire explicitement en lui envoyant le message value.
On a un exemple ici, on a un block 2+6, et puis on lui
envoie le message value. Cette fois-ci, les expressions à
l'intérieur vont être évaluées et on va nous rendre le résultat: 8.
Par contre, si l'une des expressions à l'intérieur du
block contient une erreur, évidemment l'erreur va être
retournée au moment de l'évaluation du block.
C'est l'exemple numéro 2 ici.
Si 1 est divisé par 0, j'envoie le message value au block
et on a bien une erreur qui est retournée.
Les blocks en fait peuvent aussi avoir des arguments,
donc là c'est le cas dans cet exemple comme pour les méthodes.
Le block est défini, il est toujours ouvert par un
crochet ouvrant et fermé par un crochet fermant.
On a l'ensemble des arguments qui sont ici préfixés par des deux points.
Et puis ensuite, on a une barre verticale, donc un |, qui
permet de séparer la partie déclaration des arguments de
la partie corps du block, donc l'ensemble des expressions qu'il contient.
L'argument s'appelle x dans cet exemple et puis x+2, c'est
la seule expression que contient le block.
Si j'envoie le message value au block.
Ici, il faut utiliser un message particulier donc c'est
value: avec un paramètre, puisqu'on va lui passer en même
temps la valeur 5 qui va se substituer à l'argument x au
moment de l'évaluation des expressions dans le block.
Si ce block, je lui envoie le message value: 5, on va
bien avoir le résultat 7 puisque x vaudra 5 pendant l'évaluation
de ce block.
Ça, c'est l'exemple que j'ai montré juste avant sauf que
dans ce nouvel exemple, on a plusieurs expressions dans le block.
Ici, j'en ai 2, donc x + 33 et x + 2.
Sauf que quand j'envoie le message value: 5 pour évaluer
les expressions de ce block, en fait ce qui va être
retourné par l'évaluation, c'est uniquement la valeur de
la dernière expression du block.
Ici, c'est seulement le résultat de x + 2 qui va être
retourné, donc 7.
Les blocks sont des objets comme les autres en Pharo.
Ils peuvent être sauvegardés dans des variables
temporaires et puis on peut leur envoyer des messages
comme pour des objets classiques. Donc là, c'est l'exemple
que je vous montre ici.
On peut stocker la définition de ce block x + 2 dans une
variable qui s'appelle add2 et puis ensuite, on va lui
envoyer des messages pour évaluer plusieurs fois ce block.
Je vais lui envoyer une première fois le message value:
en lui disant de s'évaluer avec la valeur 5, donc ça va nous rendre 7.
Et puis une deuxième fois, le message value: avec la
valeur 33 où ça va nous rendre la valeur 35.
On peut aussi définir des blocks avec plusieurs arguments.
Ici, je vous donne un exemple avec x et y, donc il y a
deux arguments pour ce block.
Par contre, comment est-ce qu'on pourrait faire pour
évaluer ce block, puisqu'il faudrait maintenant passer
deux valeurs pour déclencher l'évaluation, à la fois 5 et
7 qui viendraient se substituer à x et y?
La réponse, c'est qu'en fait on va utiliser le message
value: value: qui est une méthode de la classe
block. 5 va bien se substituer à x et 7 va se substituer
à y et on aura bien le résultat 12.
Comme pour les méthodes, un block peut aussi avoir des
variables temporaires. Ici, je vous donne un exemple,
mais cette fois, c'est un exemple en conditions réelles
puisqu'il est extrait de la classe Collection.
Le block commence ici et se termine ici.
Ce block a un argument qui s'appelle index.
Et puis, il a une variable temporaire définie entre deux
barres obliques ici, qui s'appelle args.
Cette variable temporaire args n'existe que
pendant l'évaluation des expressions du block.
Les blocks sont définis dans des méthodes.
Donc dans un block, on peut aussi utiliser le return.
Je vous donne un exemple ici issu de la classe Integer, c'est
la méthode qui s'appelle factorielle.
On a plusieurs blocks dans cette méthode: un ici, un ici.
Et puis ces blocks contiennent des return, c'est le symbole qui est ici.
Le return va permettre de sortir de la méthode factorielle dans cet exemple.
Par exemple, si j'envoie le message factoriel 0, j'obtiens
bien la réponse 1 et j'obtiens 1 en fait grâce au return
qui est ici le premier return dans le block qui permet de
faire sortir de la méthode factorielle.
Le return dans un block permet de sortir de la méthode
qui contient la définition du block.
Un petit conseil quand vous utilisez des blocks.
Les blocks sont des éléments extrêmement puissants.
Il faut les utiliser avec parcimonie.
Il ne faut pas aller au-delà de deux à trois arguments sur un block.
Si on va plus loin, ça devient vite incompréhensible.
Il vaut mieux définir une classe avec des variables d'instances
et des méthodes où ça sera beaucoup plus lisible.
En résumé, dans cette séquence, on a vu les blocks, on a vu leur syntaxe.
On a vu que c'étaient des sortes de méthodes anonymes, c'est-à-dire
des fermetures lexicales.
On peut stocker les blocks dans des variables, c'est des
objets comme les autres en Pharo.
On verra dans les prochaines séquences que les blocks
sont à la base d'énormément de constructions en Pharo:
les boucles, les itérations, etc.