A Smalltalk programozási nyelv

Kifejezések

Mint már említettük, az objektumok kommunikációja kizárólag üzeneteken keresztül történik, a kifejezések csak ezekből épülnek fel. Nézzük most ezt közelebbről!

Üzenetek

fogadóobjektum üzenetnév1 argumentum1 üzenetnév2 argumentum2 ...

A fogadóobjektum amelynek az üzenetet elküldjük, amelytől elvárjuk, hogy valami műveletet hajtson végre ennek hatására. Az üzenet neve állhat egy vagy több részből: a fenti példán az üzenetnév1 és üzenetnév2 ugyanannak az üzenetnek két darabja. Minden darab után egy objektumnak mint argumentumnak kell állnia, kivételt képezhet olyan üzenet, amely neve egy részből áll. Az üzenet teljes nevét szelektornak is szokták nevezni. Maga az üzenet tehát meghatározza a fogadót, a szelektort, és az argumentumokat. Ezek után az üzenetek három csoportját különböztetjük meg:

Unáris üzenetek

Ha az üzenetnek egyetlen implicit argumentumot adunk át, a fogadó objektumot, akkor unárisnak nevezzük. Az üzenet neve ekkor megfelel az azonosítókkal szemben támasztott követelményeknek, és illik kisbetűvel kezdeni. A hívás formája ekkor nagyon hasonlít a más nyelvekből megszokott szintaktikára. Az objektum neve után egy szóközzel (nem ponttal) elválasztva írjuk a metódus nevét. Például az alábbi utasítás eredményeképpen az i változóban tárolt szám abszolút értékét kapjuk meg: i abs

Bináris üzenetek

Ha egy argumentumot akarunk küldeni, és az üzenet neve a műveleti jelekből áll össze, akkor bináris üzenetről beszélünk. Első ránézésre ezek infix jelölésű műveleteknek tűnnek, de természetesen ezek is üzenetek. Az alábbiakban felsoroljuk a gyakrabban használt műveleteket:

Aritmetikai üzenetek
+Összeadás
*Szorzás
/Racionális osztás (Az eredmény egy Fraction típusú racionális szám.)
//Egész osztás
\\Modulusképzés
%Modulusképzés, a másik irányba történő kerekítéssel
**Hatványozás
<<Bitenkénti lépetetés balra
>>Bitenkénti lépetetés jobbra
Összehasonlító üzenetek
<Kisebb
>Nagyobb
<=Kisebb-egyenlő
>=Nagyobb-egyenlő
=Egyenlő
~=Nem egyenlő
==Azonos (ugyanarra az objektumra mutat)
~~Nem azonos
Logikai
|Logikai vagy
&Logikai és
Egyéb
,Konkatenálás
@Számpár képzése (2D-pont)

Azonban ezek csak a leggyakoribb bináris üzenetek, hiszen mi is bármilyent létrehozhatunk, és bármilyen műveletet rendelhetünk hozzá. Csupán a névre van pár megkötés:

Az alábbi példában a 3 objektumnak küldjük a + üzenetet a 4 paraméterrel: 3 + 4. Az eredmény a 7 egész számot tartalmazó objektum.

Kulcsszavas üzenetek

Ha explicit paramétereket is átadunk a metódusnak, akkor egy kicsit érdekesebb szintaxist kell használnunk. A metódust a paraméternevek azonosítják, azaz híváskor csak "paraméternév: paraméterérték" párokat sorolunk fel. Az üzenet szelektora ekkor a paraméternevek konkatenációjából adódik, pl.: #név1:név2:név3:. Példák:

Kiértékelés

Mi történik egy üzenet küldésekor? Először az üzenetet fogadó objektum, majd az üzenet, végül pedig a paraméterek kiértékelése történik meg (ez utóbbi rekurzívan). Az üzenet alapján végrehajtandó metódus meghatározása az öröklődési hierarchiában felfelé haladva történik. Ha az Object osztályig eljutottunk, és annak sincs ilyen üzenete, akkor runtime error lép fel. A metódusoknak mindig van visszatérési értéke, ami alapértelmezés szerint, maga az objektum. Ilyen módon az üzenetek egymásba ágyazhatóak: az első eredményeképpen kapott objektumnak újabb üzenetet küldhetünk. Pl: ((list at: 42)+1) tan sqrt rounded.

A Smalltalk szereti erősen megkülönböztetni az objektumnak küldött üzenetet az ennek hatására végrehajtott metódustól, bár erről a programozónak általában nem kell tudnia.

Prioritási sorrend

Az üzenetek kiértékelési sorrendjét az alábbi szabályok határozzák meg:

  1. Az unáris kifejezések balról jobbra kötnek.
  2. A bináris kifejezések balról jobbra kötnek.
  3. Az unáris kifejezéseknek nagyobb a precedenciájuk, mint a bináris kifejezéseknek.
  4. A bináris kifejezéseknek nagyobb a precedenciájuk, mint a kulcsszavas kifejezéseknek.
  5. A zárójelezett kifejezéseknek nagyobb a precedenciájuk, mint az unáris kifejezéseknek.
  6. Ha egy kulcsszavas üzenet meghatározásakor az összes kulcsszót hozzáveszi az üzenethez.

Ez a kiértékelés kissé szokatlan, ezért nézzünk néhány példát rá:

Kifejezés:Értelmezése:Amit nem jelent:
index + offset * 2 ( index + offset ) * 2 index + ( offset * 2 )
2 * theta sin 2 * ( theta sin ) ( 2 * theta ) sin
bigFrame width: smallFrame width * 2 bigFrame width: ( ( smallFrame width ) * 2 ) ( ( bigFrame width: smallFrame ) width ) * 2
frame scale: factor max: 5 frame objektumnak a #scale:max: üzenetet küldjük frame scale: ( factor max: 5 )
( frame scale: factor ) max: 5

Kifejezés-sorozatok

Az ugyanarra az objektumra vonatkozó, egymást követő metódushívások összevonhatók egy sorozatba a pontosvessző segítségével. Tehát, ha egy üzenetben nem szerepel a fogadó neve, és az előző kifejezést egy pontosvessző zárja le, akkor a fogadó az előző kifejezésben utoljára elküldött üzenet fogadója lesz (nem pedig az üzenet válasza). Akkor használjuk, ha ugyanannak az objektumnak több üzenetet akarunk küldeni, és nem érdekel minket az üzenetek válaszai. Például:

Turtle black; home; go: 100; turn 120; go: 100; turn: 120; go: 100; turn: 120; black; home. t := (Array new: 3) at: 1 put: 1 + 3; at: 2 put: 3 + 5; at: 3 put: 'Hello! Hogy vagy?'; yourself

A yourself hatására a t nem az utolsó üzenet eredményét kapja értékül, hanem a tömböt.

1+2*4;*5 eredménye 15, hiszen legutoljára a *4 metódust hajtottuk végre a 3 objektumra (ami az 1+2 eredménye volt).