Structures de contrôle en Smalltalk
icone programmation
Signature : ,
Linux Pratique
Sommaire de l'article :
Dans ce quatrième article (et dernier article de la série) sur Squeak, nous allons nous intéresser aux structures de contrôle. Nous verrons qu'il n'y a pas de syntaxe spécifique pour les structures de contrôle conditionnelles (si-alors-sinon) ou itératives (tant-que) en Smalltalk à la différence d'autres langages classiques et que les structures de contrôle itératives sont fortement liées à la notion de "Collection".

Blocs et structures de contrôles

Il n'est pas rare d'entendre des gens ne connaissant pas Smalltalk dire que les structures de contrôles n'existent pas en Smalltalk ! C'est bien sûr ridicule. En fait, toutes les structures de contrôle existent, par contre elles sont définies sous forme d'envois de messages à des objets (booléens, fermetures, entiers ou intervalles). De plus, il est tout à fait possible de définir ses propres structures de contrôle si cela est nécessaire. Smalltalk introduit la notion de "blocs" (ou fermetures lexicales). Il s'agit d'objets qui contiennent du code qui peut être évalué ultérieurement et ainsi transmis comme n'importe quel autre objet. Ces objets disposent d'arguments permettant de paramétrer le code lors de l'évaluation. Un bloc peut être vu comme une méthode anonyme pouvant être décrite dans n'importe quelle expression et passée en argument ou retournée comme résultat.
  • Exemple :
x := [3+4].
x value.
L'envoi de message value évalue le bloc et retourne le résultat de la dernière expression du bloc. Les blocs peuvent être affectés à des identificateurs, passés en paramètre comme on le verra pour les expressions conditionnelles. Il est possible d'avoir des blocs paramétrés également. Ce sont alors de véritables fonctions :
1: |f |
2: f := [:v | v+1].
3: g := [:x :y | x+y].
4: f value: 1.
5: g value:1 value:2.
La ligne 4 retourne comme valeur 2, la ligne 5 retourne 3. Le code suivant génère trois sons différents suivant le paramètre du bloc :
block := [ :soundName | (SampledSound soundNamed: soundName) play].
block value: #croak.
block value: #scratch.
block value: #chirp
Il est également possible de définir des variables temporaires à l'intérieur d'un bloc en les entourant à l'aide des barres verticales ||.
  • Exemple :
f := [ :x |
|pi|
pi := -1 arcCos.
(2 * x * pi / 3) cos].
f value: 2

Conditionnelles

Les expressions conditionnelles (si-alors-sinon) utilisent les blocs et sont de la forme suivante : condition ifTrue:[unPremierBloc] ifFalse:[unDeuxièmeBloc] Si la condition retourne la valeur vrai (true), le premier bloc est exécuté, sinon c'est le deuxième bloc qui est exécuté. Il existe également deux formes simplifiées du ifTrue:ifFalse: comme le montrent les exemples ci-dessous :
1 < 2 ifTrue: [Transcript show: 'vrai'].
1 > 2 ifFalse:[Transcript show: 'faux'].
1 > 2 ifTrue:[Transcript show:'1'] ifFalse:[Transcript show:'2'].
On obtient successivement dans la fenêtre de Transcript : vrai, faux, et enfin 1. Avec ce que nous avons déjà vu, vous pouvez en déduire que ifTrue:, ifFalse:, ifTrue:IfFalse: sont des envois de messages à mots-clés qui sont envoyés à des instances de la classe Boolean (ici le résultat de la comparaison de 1 et de 2). Il est également possible de comprendre comment les expressions conditionnelles sont implémentées en Smalltalk en examinant le code des méthodes : ifTrue:, ifFalse:, ifTrue:ifFalse: au niveau des classes True et False. Pour cela, ouvrons deux mini-navigateurs de classe sur la classe True par exemple : Fig. 1. C'est relativement simple. La méthode ifTrue: définie sur la classe True est exécutée quand le receveur du message ifTrue: est true. Dans un tel cas, elle se contente d'évaluer le bloc passé en paramètre. De la même manière ifFalse: définie sur cette même classe ne fait rien car le receveur n'est pas faux mais vrai, donc rien ne doit se passer. De la même manière, la méthode ifFalse: définie sur la classe False exécutera son argument et la méthode ifTrue: ne fera rien. À vrai dire, peu de langages permettent de faire cela ! Maintenant pour rassurer les gens pensant que cela est coûteux en termes d'exécution, il faut savoir que le compilateur optimise de façon drastique ces envois de messages dans le code généré.

Itérations

De la même façon, les itérations sont des messages envoyés à divers objets (nombres, fermetures) suivant les mêmes règles syntaxiques que celles des messages. Une simple répétition s'écrit en envoyant le message timesRepeat: à un nombre et en passant le code à répéter. 4 timesRepeat: [ Transcript show: 'hello' ;cr ] écrit 4 fois 'hello' dans la fenêtre Transcript. L'itération de type tant-que peut s'utiliser facilement en utilisant deux blocs sans paramètre :
| f n |
f := 1.
n := 4.
[ n>1 ]
whileTrue: [ f := f*n. n := n-1 ].
f.
Tant que la condition dans le premier bloc est vérifiée, le deuxième bloc est effectué. Notez que le message whileTrue: est envoyé à un objet booléen retourné par le bloc [n > 1] alors que le message ifTrue: est envoyé à un objet booléen retourné par une expression. Ici, on calcule la factorielle de 4 : f = 1*2*3*4 = 24. Voici un autre exemple d'itération, où l'on effectue n fois le bloc paramétré en faisant varier à chaque fois, la valeur de i de 1 à 100 :
|somme|
1 to: 100 do: [:i | somme := somme + i*i].
somme.
On calcule ici la somme des 100 premiers entiers.

/img-articles/lp/33/art-13/fig-1.jpg

Collection et itérations

De très nombreuses autres formes d'itération existent en Smalltalk, la plupart utilisent des parcours de la classe Collection. Une collection est un objet qui regroupe un ensemble d'objets de même nature. La classe Collection est la super-classe de toutes les collections. De très nombreuses sous-classes implémentent des collections qui diffèrent par leur moyen d'accès en lecture/écriture et par leur performance. Les plus courantes sont :
  • les tableaux (Array) ;
  • les ensembles (Set) ;
  • les multi-ensembles (Bag) ;
  • les collections ordonnées (OrderedCollection) ;
  • les collections triées (SortedCollections) ;
  • les dictionnaires (Dictionary) ;
  • les symboles (Symbol) ;
  • et chaînes de caractères (String).
Les instances de la classe Array ont un nombre d'éléments de taille fixe auquel on peut accéder au moyen d'un indice entier. On crée un tableau au moyen de la notation : #( ... ).
1: | t |
2: t := #(10 3 5 8 5).
3: t size.
4: t at: 2.
5. t at:1 put:7.
A la ligne 1, on crée une variable t. On associe à cette variable, un tableau d'entiers. On calcule le nombre d'éléments à la ligne 3, puis à la ligne suivante, on récupère l'élément du tableau qui se trouve à la position 2. Il s'agit de l'entier 3, puis enfin on remplace le premier élément du tableau par la valeur 7. Bag et Set sont des collections permettant de représenter respectivement des multi-ensembles ou des ensembles de valeurs dans lesquels il n'y a pas d'ordre particulier. La différence essentielle entre ces deux collections est que dans un ensemble, il ne peut y avoir qu'une fois et une seule fois chacune des valeurs, alors que le multi-ensemble autorise plusieurs valeurs identiques.
| b s |
b := Bag new.
b add: Color red; add: Color green; add: Color yellow; add: Color yellow.
b size. => retourne 4
s := b asSet. => transforme le multi-ensemble en ensemble
s size. => retourne 3, une des instances yellow de la classe Color a été supprimé.
s includes: Color red => retourne true.
Une collection OrderedCollection ajoute la notion d'ordre (premier, deuxième, dernier,...) entre les éléments mais, à la différence d'un tableau, le nombre d'éléments n'est pas fixé à la création de la collection. Une collection SortedCollection est une collection OrderedCollection où les éléments sont triés suivant un ordre déterminé par l'utilisateur.
| o s |
o := OrderedCollection new. => construit une collection ordonnée vide
o addAll: #(2 1 5 6). => ajoute dans la collection, les éléments du tableau
o addFirst: 3. => ajoute 3 en tête de la collection
o addLast: 4. => ajoute 4 en fin de collection
o size. => il y a 6 éléments dans la collection o
s := o asSortedCollection. => transforme la collection en collection triée : 1 2 3 4 5 6
s sortBlock:[:a :b| a > b]. => on modifie la façon de trier les éléments
s. => retourne la collection triée : 6 5 4 3 2 1
Un dictionnaire (Dictionary) est une collection dont les éléments peuvent être accédés au moyen d'une clé (un symbole), un peu de la même façon dont on accède habituellement à un dictionnaire ou une encyclopédie.
| translate |
translate := Dictionary new.
translate at:#un put:'one'.
translate at:#deux put:'two'.
translate at:#un. => retourne la chaîne de caractère : 'one'.
Symboles (Symbol) et chaînes de caractères (String) sont également des collections :
'Hello World' size. => retourne 11 (la taille de la chaîne de caractère)
'Hello World' at: 1. => retourne $H
#HelloWorld includes:$W. => retourne true
On voit que chaque Collection a des caractéristiques spécifiques mais aussi partage de nombreux messages : size, includes:, at:, at:put:,... Un des usages les plus courants et les plus puissants des collections est de pouvoir les parcourir afin d'effectuer un traitement sur chacun des éléments.
#(19 69 15 10 44 78) do: [:each | Transcript show: each printString ;cr].
affiche tous les éléments de la collection dans le Transcript. En fait, do: applique à chaque élément le bloc passé en argument. Notez que l'on aurait pu utiliser :element ou tout autre nom à la place de :each qui est juste le nom du seul paramètre du bloc comme le wmontre le bloc suivant [:element | Transcript show: element printString ;cr] Le message collect:
#(19 69 15 10 44 78) collect: [:each | each odd ].
=> #(true true true false false false)
envoie le message odd (qui demande à un objet s'il est impair) à chaque élément de la collection et retourne une collection contenant tous les résultats. La collection retournée est de même nature que celle sur laquelle on applique la méthode collect:.
  • Le message select:
#(19 69 15 10 44 78) select: [:each | each odd ].
=> #(19 69 15)
sélectionne tous les éléments de la collection qui sont impairs. *  Le message reject:
#(19 69 15 10 44 78) reject: [:each | each odd ].
=> #(10 44 78)
retourne tous les éléments de la collection n'étant pas impairs. C'est l'opération duale de select:. Essayons d'utiliser les collections et les itérations sur un exemple plus complexe. Comment répondre à la question suivante : quelle est la classe Squeak qui possède le plus de méthodes ?
| s |
s := ProtoObject allSubclasses.
On récupère dans la collection s, l'ensemble de toutes les classes sous-classe de la classe ProtoObject (racine du graphe d'héritage de Squeak). En faisant s size, on obtient 3780 classes. Parmi ces 3780 classes, déterminons celles qui ont plus de 800 méthodes :
s select: [:classe | classe methodDictionary size > 800].
Chaque classe est un objet (tout est objet en Smalltalk !). Nous récupérons son dictionnaire de méthodes au moyen de la méthode methoDictionary, puis nous calculons la taille de ce dictionnaire. L'itération select: sélectionne uniquement les classes dont le nombre de méthodes dépasse 800. Elle retourne une collection de type Set ne contenant qu'un seul élément. Il s'agit de la classe Morph. On peut connaître le nombre exact de méthodes de la classe Morph :
Morph methodDictionary size. => 1129
Dernier point de syntaxe, dans une méthode, la flèche vers le haut (^) rend le résultat de l'expression qui la suit à l'objet ayant invoqué la méthode. C'est l'équivalent du return en Java par exemple. Une méthode retourne forcément une valeur en Smalltalk. Si aucune valeur n'est retournée, c'est la valeur self qui est renvoyée.

En conclusion

Comme vous le voyez, parcourir des collections est extrêmement simple et élégant en Smalltalk. Sachez d'autant plus que ces méthodes sont définies polymorphiquement et donc s'appliquent aussi bien aux tableaux, qu'aux listes, ensembles, sacs, collections ordonnées et autres structures de données. Les structures de contrôle ne font pas directement partie de la syntaxe du langage Smalltalk. Ce sont des envois de messages spécifiques ! Pour ceux qui voudraient continuer dans l'apprentissage du langage Smalltalk, nous vous recommandons de lire les pages du wiki de la communauté francophone et de vous abonner à la liste de diffusion squeak-fr (voir ci-dessous). Vous lirez notamment avec profit, les pages concernant le modèle objet de Smalltalk (où tout est objet, y compris les classes comme on a pu le voir dans le dernier exemple), ainsi que la présentation des différents outils de développements : navigateur de classes, inspecteurs d'objets, outils de refactorisation et de versionning du code source. LIENS :
Il y a : 0 commentaire(s)

Donnez votre avis

Vous devez avoir ouvert une session pour écrire un commentaire.

Brèves
Édito : Linux Pratique Essentiel N°24
Édito : Linux Pratique HS N°23
Édito : GNU/Linux Magazine 146
Édito : GNU/Linux Magazine HS N°58
Édito : Open Silicium N°5
Communication
Linux Pratique HS 23 – Communiqué de presse
Linux Pratique Essentiel N°24 – Communiqué de presse
Gnu/Linux Magazine sponsor et partenaire de PROLOGIN
Linux Essentiel partenaire des Rencontres du Libre de Lion sur Mer (Normandie)
GNU/Linux Magazine HS 58 – Communiqué de presse
prochainement moteur de recherches des articles
 
:
:
Jours heures minutes secondes
En kiosque
Le tout nouveau Linux Pratique Essentiel est disponible dès maintenant chez votre marchand de journaux et sur notre site...

Lire la suite...

Le tout nouveau Linux Pratique est disponible dès maintenant chez votre marchand de journaux et sur notre site...

Lire la suite...

Le tout nouveau GNU/Linux Magazine est disponible dès maintenant chez votre marchand de journaux et sur notre site...

Lire la suite...

Le tout nouveau GNU/Linux Magazine HS est disponible dès maintenant chez votre marchand de journaux et sur notre site...

Lire la suite...

Le tout nouveau Open Silicium est disponible dès maintenant chez votre marchand de journaux et sur notre site...

Lire la suite...

Le tout nouveau Linux Pratique est disponible dès maintenant chez votre marchand de journaux et sur notre site...

Lire la suite...

Le tout nouveau Misc est disponible dès maintenant chez votre marchand de journaux et sur notre site...

Lire la suite...

Le tout nouveau GNU/Linux Magazine est disponible dès maintenant chez votre marchand de journaux et sur notre site...

Lire la suite...