Résumé du cours du mercredi 10/3/2004

 

Syntaxe

Les transmissions de messages sont constituées d’un récepteur et d’un message.

Le récepteur est l’objet qui reçoit le message.

 

Par exemple :

11 printString

 

11 est le récepteur du message, printString est le message.

 

Les messages sont de trois types :

1/ Les message unaires

2/ Les messages binaires

3/ Les messages à mots clefs.

 

Messages unaires

Les messages unaires ne concernent qu’un objet, le récepteur. Ils sont prioritaires sur les autres types de message.

Exemples de messages unaires :

1 class

 

Set new

 

Les messages unaires peuvent être enchaînés :

pe gapp memoire adresse class

« Quelle est la classe de l’adresse de la mémoire du gapp du pe ? »

Le récepteur du premier message est « pe » (supposé être un processeur élémentaire), le message « gapp » lui est envoyé. La valeur de retour de ce premier message sert alors de récepteur au second message « memoire », etc...

 

Les messages unaires sont constitué d’un symbole commençant par une lettre minuscule, et ne contenant que des lettres et chiffres, sans caractères spéciaux.

 

Messages binaires

Les messages binaires, comme leur nom l’indique, concernent deux objets : le récepteur, et un argument.

Exemple :

1 + 2

Le récepteur est « 1 » et l’argument « 2 ».

Les messages binaires sont constitués de 1 ou deux caractères spéciaux, comme + - / ! \ * @ ,

 

Les messages binaires sont exécutés après les messages binaires.

Par exemple :

1 + 5 factorial

La factorielle de 5 est calculée en premier, et le résultat de la factorielle est utilisé en argument du message d’addition.

 

Lorsque les messages binaires sont combinés, il sont exécuté de gauche à droite, avec la même priorité :

1 + 4 * 3 + 2 ó(((1 + 4) * 3) + 2)

 

Les parenthèses permettent de modifier l’ordre d’exécution :

1 + (4 * 3) + 2 à La multiplication sera faite en premier

 

Messages à mots clefs

Les mots clefs sont constitué d’un symbole terminé par un deux points ‘:’.

Par exemple :

#(56 34 23) at: 1

 

Le message « at: » est constitué d’un seul mot clef, permettant l’introduction d’un argument.

Les message « at:put: » est constitué de deux mots clefs, permettant de donner deux arguments au message.

#(56 34 23) at: 1 put:13

 

Les signes deux points font partie intégrante du nom du message (appelé sélecteur).

Les messages à mot clef sont les moins prioritaires.

 

Lorsqu’on doit combiner plusieurs mots clefs dans une même expression les parenthèses sont obligatoires.

#(‘0’ ‘1’ ‘11’ ‘11’) at: 3 put: (2 printStringRadix: 2)

Si les parenthèses étaient absentes, le compilateur interprèterai un message at:put:printStringRadix: unique avec trois arguments !

 

Les descriptions littérales

Les objets constituant les récepteurs ou les arguments des messages peuvent être les résultats d’une transmission ou le contenu d’une variable, ou encore écrits directement (littéralement (avec des lettres)).

Par exemple dans l’expression :

#(‘0’ ‘1’ ‘11’ ‘11’) at: 3 put: (2 printStringRadix: 2)

Le tableau récepteur du message at:put: est décrit littéralement.

Les objet 3 et 2 également, alors que le second argument de at:put: est le résultat d’une expression.

 

Quelques description littérales :

 

Classe

Description Littérale

Array

#(1 2 3 4)

Array

#(1 (3))

Character

$a

String

’chaîne’

Symbol

#nomDeChose

Symbol

#’e s p a c e’

SmallInteger

23445

Float

1.5

Double

1.5d

Block

[]

Block

[:x | x*x]

 

 

Les blocs

Les blocs sont des objets capables de mémoriser des suites d’instructions.

Les blocs sont formés syntaxiquement en plaçant les instructions entre crochets.

Par exemple :

[ Transcript show: 'Un texte à afficher' ; cr ]     

Si on évalue cette expression, le résultat sera un bloc, le texte ne sera pas affiché.

Pour déclencher l’exécution du bloc, il faut lui envoyer un message « value ».

[ Transcript show: 'Un texte à afficher' ; cr ] value

Cette fois, le texte s’affichera dans la fenêtre de « Transcript » de VisualWorks.

 

Les blocs sont des objets comme les autres, on peut donc les mettre dans des variables ou les passer en argument de messages.

Par exemple :

Transcript show: (1 < 100 ifTrue: [ 'oui' ] ifFalse: [ 'non' ]) ; cr

 

| a | a := 7 \\ 3 = 0 ifTrue: [ 4 * 5 ] ifFalse: [ 6 * 7 ].

^ a

 

Les méthodes ifTrue:ifFalse: sont définies de façons différentes dans les classes True et False.

Pour la classe True :

ifTrue: b1 ifFalse: b2

     ^ b1 value

 

Pour la classe False :

ifTrue: b1 ifFalse: b2

     ^ b2 value

 

Ceci est le mécanisme principal permettant d’implémenter des structures de contrôle par la transmission de message.

Si le récepteur du message est true, le premier bloc est évalué et le second ignoré.

A l’inverse, si false est le récepteur c’est le premier bloc qui sera ignoré et la valeur du second sera retournée.

 

A partir de ce mécanisme et d’une écriture de méthode récursive pour les entiers, nous implémentons un message pour répéter l’exécution d’un bloc :

 

Integer

repeter: actions

            self > 0 ifTrue: [ actions value. self - 1 repeter: actions ]

 

Un message similaire est déjà présent dans Smalltalk : le message timesRepeat:

Les blocs peuvent prendre des paramètres

On pourrait représenter une fonction par un bloc :

| f y |

f := [ :x | 3 * x + 1 ].

"Le bloc f représente la fonction d’équation y = 3x+1.

Pour calculer la valeur de y en fonction de x, il faut évaluer le bloc f avec le message value: qui permet de spécifier une valeur pour le paramètre du bloc."

y := f value: 23

Si le bloc possède deux paramètres on l’évalue avec le message value:value:.

Si le bloc possède trois paramètres on l’évalue avec le message value:value:value:.

 

Exemple :

[ :x :y | x * y squared ] value: 2 value: 3

Le bloc est évalué avec x valant 2 et y valant 3.

 

 

Programme utilisant les Blocs

Le programme suivant utilise les blocs pour mémoriser deux actions et alterner entre ses deux actions à chaque exécution.

 

En premier nous définissons une classe, nommée Alternateur, avec deux variables d’instance action1 et action2 pour mémoriser les deux actions.

 

Object subclass: #Alternateur

            instanceVariableNames: 'action1 action2 '

            classVariableNames: ''

            poolDictionaries: ''

            category: 'VL-Alternateur'!

 

Une méthode d’accès permet de donner à une instance les deux blocs d’action à mémoriser.

 

!Alternateur methodsFor: 'accessing'!

 

action: b1 action: b2

            "Memoriser les deux blocs d'actions"

            action1 := b1.

            action2 := b2!

 

La méthode « agir » déclenche l’action 1 puis permute les deux actions.

agir

            action1 value.

            self permuter!

 

Enfin, la méthode permuter échange les blocs d’actions, en plaçant l’action 1 en second et l’action 2 en premier, de sorte que le prochain message « agir » exécutera l’alternative.

 

permuter

            self action: action2 action: action1! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

 

Au niveau de la classe, une méthode donne l’exemple :

Alternateur class

            instanceVariableNames: ''!

 

 

!Alternateur class methodsFor: 'exemple'!

 

exemple1

            | c lampe |

            lampe := Lampe eteinte.

            c := Alternateur new

                        action: [ lampe allumer. Transcript show: lampe printString; cr ]

                        action: [ lampe eteindre. Transcript show: lampe printString; cr ].

            10 timesRepeat: [ c agir ].! !

 

Pour l’exemple, nous créons une classe Lampe dont les instances peuvent être allumées ou éteintes (ce n’était pas dans le cours) :

 

Object subclass: #Lampe

            instanceVariableNames: 'allumee '

            classVariableNames: ''

            poolDictionaries: ''

            category: 'VL-Alternateur'!

 

 

!Lampe methodsFor: 'accessing'!

 

allumee

            ^ allumee!

 

allumer

            allumee := true!

 

eteindre

            allumee := false! !

 

!Lampe methodsFor: 'printing'!

 

printOn: aStream

            aStream nextPutAll: '(Lampe ', (allumee ifTrue: [ 'allumee'] ifFalse: [ 'eteinte' ]), ') '! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

 

Lampe class

            instanceVariableNames: ''!

 

 

!Lampe class methodsFor: 'instance creation'!

 

allumee

            ^self new allumer!

 

eteinte

            ^self new eteindre! !