Nem frissíthető és frissíthető változók

12.1. Kérdés.
Mi a különbség pontosan az = és := között? Szeretném azt is megtudni, hogy a háttérben pontosan mit csinál az interpreter, vagyis pl. mi történik val v1 = v2-vel? Csak egy mutatót rak v2 értékere? Mi lesz, ha ezután val v1 = 4-et adunk? Valahol leteszi a 4-est, és átirányítja a v1 mutatóját?

12.1. Válasz.
Oktató: Az egyenlőségjel (=) egy deklarációban a deklarálandó nevet választja el a névhez kötendő kifejezéstől. A legyen-egyenlő jel (:=) egy értékadásban választja el a változó nevét attól az értéktől, amelyet a változó által megnevezett memóriacellába kell beírni.

Az SML-ben a val deklarációval létrehozott dolgot nem változónak, inkább névnek (name) vagy azonosítónak (identifier) nevezzük, hogy megkülönböztessük az imperatív nyelvek változófogalmától. Az SML-beli név a matematikai változó fogalmának felel meg, ezért szokták változónak (variable) is nevezni, de ez, mint írtam, félrevezető. Ha mindenképpen változónak akarjuk nevezni, nevezzük funkcionális változónak. A funkcionális változó nem frissíthető (szemben az imperatív nyelvek frissíthető változójával), és nem értékadással, hanem a deklaráció által létrehozott kötéssel kap értéket.

A név tehát az SML-ben a deklaráció jobb oldalán álló érték megnevezésére, azonosítására szolgál, azaz az érték egy szinonimája. Az mosml értelmező a nevet a hozzá kötött értékkel együtt egy táblában tárolja; ha a deklarációban egy kifejezést kötünk egy névhez, akkor az mosml a mohó kiértékelésnek megfelelően azonnal kiértékeli a kifejezést, és az eredményül kapott értéket írja be a táblába. Egy név szinonima-volta azt jelenti, hogy valahányszor a név előfordul egy kifejezésben, az értelmező a nevet azonnal lecseréli a hozzá kötött értékre. Mutatóról, hivatkozásról tehát ilyen esetben szó nincs.

Lássuk akkor a példákat arra, hogy mi történik a háttérben! A val v1 = v2 deklaráció hatására az mosml bejegyzi a táblába a v1 nevet és hozzá köti a v2 értékét (nem egy mutatót, nem a v2 nevet, hanem a v2 névhez kötött értéket). Nyilvánvaló, hogy ha a v2-nek nincs értéke, a deklaráció nem értékelhető ki, az mosml hibát jelez. Ha az mosml ezután a val v1 = 4 deklarációt értékeli ki, akkor a táblába a v1 név mellé a 4 értéket írja be, és a továbbiakban a v1-et a 4 szinonimájaként használja. Fontos, hogy a v1 korábbi előfordulásait a változás nem érinti, a v1-et tartalmazó korábbi kifejezésekbe ugyanis a v1 korábbi értéke (azaz a v2 akkori értéke) már ,,beépült''.

Az SML-ben is vannak frissíthető változók, mert az SML nem tisztán funkcionális nyelv. Mivel a Deklaratív programozás tárgy a deklaratív, azaz a logikai és a funkcionális programozási stílussal szeretné ,,megfertőzni'' a hallgatókat, az SML imperatív ficamairól az előadásokon inkább szemérmesen hallgatunk. A kérdésre a választ mégsem akarom elsumákolni, ezért röviden összefoglalom a dolgot. Akinek ez nem elég, az mosml-lel együtt letöltött és az mosml-honlapon is megtalálható General.sig-ben és a Moscow ML Language Overview-ban utánanézhet.

Frissíthető változót az mosml-ben úgy hozhatunk létre, hogy a deklarációban a ref kulcsszóval rögtön azt is megadjuk, hogy az adott névnek mi lesz a kezdőértéke és ebből következően a típusa. Példa:

val frissitheto = ref 19;
A frissíthető változó értékét a !-lel jelölt ún. dereferencing vagy röviden deref függvény alkalmazásával csalhatjuk elő a változóból, ha a nevére alkalmazzuk. Példák:

!frissitheto;
20 - 1 = !frissitheto;
A frissíthető változónak új értéket a :=-vel jelölt értékadás-művelettel (infix operátorral) adhatunk. Példa:

frissitheto := 20;
frissitheto := !frissitheto - 1;
A kérdező folytatja: Az egyik évfolyamtársam említette, hogy a := csak az értéket írja át, a mutatót nem. Mi történik a következő esetekben?

val v1 = 1
val v2 = v1
val v1 := 2
Oktató: Azt hiszem, a fenti részletes magyarázat után már mindenki tudja a választ. Azt is, hogy a harmadik sorban szintaktikai hiba van, mert a v1 nem frissíthető változó.


Deklaratív programozás - FP-GYIK
2005. március 1.