A gráfátíró rendszerek a lusta funkcionális programozási nyelvek egyik modellje. A modell még elég absztrakt, de a segítségével már jól megállapítható az egyes funkcionális programok memória- és időigénye.
A modellben a két fő fogalom az adatgráf és a gráfátírás.
Egy adatgráf:
Az adatgráfok a következő tulajdonságokkal rendelkező gráfok:
Egy csúcs élei alatt a csúcs kimenő éleit fogjuk érteni.
Az adatgráfok a pointeres adatszerkezetek absztrakcióinak tekinthetők. Az előző adatgráf C-beli struktúrákkal megvalósítva:
A dobozok rekordokat jelölnek. A kis dobozok a rekordmezők. A szimbólumok valamilyen konkrét Int
konstanst jelölnek, ami megkülönböztethetővé teszi őket. A nyilak pointereknek felelnek meg.
Az adatgráfot szöveges formában is ábrázolhatjuk. Az adatgráf szöveges formája feltűnően fog hasonlítani a Clean vagy a Haskell nyelv egy résznyelvére, így ezt kiegészítve egy eljárást kaphatunk a funkcionális kifejezések adatgráffá konvertálására.
A szöveges formát példákon keresztül mutatjuk be.
A konstruktorokból felépített kifejezések fa szerkezetű adatgráfoknak felelnek meg.
Az elemi típusok, például az egész számok vagy a karakterek 0 argumentumú konstruktoroknak felelnek meg.
Példa:
Új szimbólumokat vezetünk be a párok, hármasok, négyesek stb. kezelésére: (,)
, (,,)
, (,,,)
, …
A listákat a (:)
és []
konstruktorokkal írjuk le.
Minden konstruktornak egy szimbólum fog megfelelni. A konstruktor paraméterei a szimbólumot tartalmazó csúcs gyerekeinek felelnek meg.
Just 12
A megosztott kifejezéseket a Haskell és Clean programokban a where
és let
.. in
kulcsszavak jelölik. (A let
a where
egymásbaágyazható megfelelője.)
Az előbbi gráf a számítás szempontjából ekvivalens a következővel:
Az osztott kifejezések a gráfban a memóriahasználat és a futási idő szempontjából jelentősek.
Vegyük észre, hogy a kifejezések mint gráfok különbözőek, de szemantikusan azonosak, mert mindkettő azonos a következő gráffal:
Példa: Egy gyűrű leírása:
A gráf csúcsaihoz rendelt szimbólumok kétfélék lehetnek:
Eddig csak konstruktor-szimbólumokat használtunk.
A függvényszimbólumok a gráfban elhalasztott számításokat jelölnek. A függvényszimbólumokat tartalmazó csúcsok gyerekei a függvény argumentumaira mutatnak.
Figyeljük meg, hogy nincs szemantikus különbség a következő gráfok között:
Tehát a függvényszimbólumokkal ugyanazt az adatot még többféleképpen leírhatjuk.
Minden konstruktor- és függvényszimbólumhoz tartozik egy szám, ami megmondja, hogy az adott szimbólumhoz tartozó csúcsnak hány élének kell lennie. Ezt a számot a szimbólum aritásának nevezzük.
Példák aritásra:
Szimbólum | Aritás |
---|---|
‘a’ | 0 |
12.3 | 0 |
True | 0 |
: | 2 |
[] | 0 |
+ | 2 |
(,) | 2 |
(,,) | 3 |
Egy csúcsnak sohasem lehet több éle, mint a benne levő szimbólum aritása. Egyes esetekben viszont lehet kevesebb él, lásd a magasabbrendű függvényekkel foglalkozó részt.
A gráfredukció, vagy egyszerűen csak redukció az elhalasztott számításokat végzi el a gráfban. Egyetlen redukciós lépés mindig pontosan egy függvényszimbólumot eliminál a gráfból. A redukciós lépés során bekerülhetnek a gráfba újabb függvényszimbólumok is.
A redukció átírási szabályok alapján történik.
Az átírási szabályok két részből állnak: egy bal és egy jobb oldalból. A bal oldal a minta, ezt illesztjük az adatgráfra, a jobb oldal pedig megmondja hogy hogyan kell módosítani az adatgráfot ha illeszkedik a minta.
Az átírási szabályokat szintén gráfokkal ábrázoljuk. Az átírási szabályoknak két gyökerük van, egy a bal oldal, egy a jobb oldal részére. Ezeket lhs és rhs-sel jelöljük (left hand side, right hand side).
Példa egy átírási szabályra:
Az átírási szabályok tartalmazhatnak változókat. A változók tetszőleges részgráfot jelölnek. A változókat háromszöggel fogjuk jelölni.
Az előző átírási szabály általánosítása:
Az átírási szabály jobb oldalán hivatkozhatunk a bal oldal részfáira:
A következő megszorításokat tesszük az átírási szabályokra:
Ezek a megszorítások biztosítják, hogy a gráfátíró rendszer konfluens legyen. A konfluencia azt jelenti, hogy ha egy adatgráfot kétféleképpen redukálhatunk, akkor nem számít, hogy melyik redukciót választjuk, mert később úgyis alkalmazható lesz a másik is, és így elérhetjük ugyanazt a közös végeredményt.
A gráfátírási lépések nem változtatják meg a kifejezések szemantikáját. Így például nincs szemantikus különbség az (1 + 2) * 5
és 3 * 5
között.
Az átírási szabályok szöveges alakját a következőképpen képezzük:
=
rhs, ahol lhs a bal oldal szöveges alakja, rhs pedig a jobb oldal szöveges alakja.Példa:
Az illesztés során egy adott szabály bal oldalát illesztjük az adatgráf egy részgráfjához. Az illesztés sikeres, ha találunk egy olyan függvényt, ami a bal oldal csúcsaihoz hozzárendeli az adatgráf csúcsait a következőképpen:
A sikeres illesztés esetén a bal oldal képét redukálható részgráfnak, vagy redexnek nevezzük.
Az illesztés leírása a ciklikus gráfok miatt ilyen körülményes.
Példa: Legyen a gráf a következő:
Legyen az illesztendő bal oldal a következő:
Az illesztés:
Másik példa: az f x where x= 11: x
gráfra illesztjük az f (a: b: c)
bal oldalt:
A grátátírás során az átírási szabály illeszkedő bal oldalt kicseréljük a jobb oldalra. Mivel lehetnek még egyéb hivatkozások is a bal oldalra a gráfban, ezért az átírást óvatosan kell végeznünk.
A gráfátírás a következő lépésekből áll:
Példa:
átírása a következőre:
A kék részek az átírás előttiek, a piros részek az átírás utániak.
Tekintsük a következő gráfátíró rendszert:
Tekintsük a következő adatgráfot:
Az első redukciós lépés hatása:
A második redukciós lépést részletesebben nézzük. Az második szabály illesztése és a jobb oldalának bemásolása után ezt látjuk (piros - bemásolt rész):
Az átirányítás után (szürke - régi élek, zöld - új élek):
A szemétgyűjtés előtt (szürke - szemét):
A szemétgyűjtés után (a második redukciós lépés végén):
A harmadik redukciós lépés után:
A negyedik redukciós lépés után:
Az ötödik redukciós lépés után:
Megjegyzés: Ha a cycle
szabálya helyett a következő (szemantikusan ekvivalens) redukciós szabályt alkalmaztuk volna
akkor egy végtelen gráf lenne a redukció végeredménye.
A megismert gráfátíró rendszerek kis változtatással alkalmassá tehetők magasabbrendű függvények kezelésére is.
Először bevezetjük a részleges alkalmazás fogalmát. Az adatgráf egy csúcsa részleges alkalmazás, ha a csúcsnak kevesebb éle van, mint a benne levő szimbólum aritása.
Példa: A következő gráfban even
részlegesen alkalmazott.
Megjegyzés: A konfluencia megtartása érdekében az átírási szabályok bal oldala nem tartalmazhat részleges alkalmazásokat.
A következő megoldandó probléma, hogy a részlegesen alkalmazott csúcsoknak hogyan adunk további éleket. A feladatot alkalmazás-csúcsok bevezetésével oldjuk meg.
Az alkalmazás csúcsokat $
szimbólummal jelöljük meg.
Példa:
Az alkalmazás csúcsokra leginkább az átírási szabályokban van szükség. Például:
Az alkalmazás-csúcsokat a következő szabályok szerint eliminálhatjuk:
(Ezek metaszabályok, mivel f
helyén tetszőleges szimbólum állhat.)
Tehát az alkalmazás-csúcsok hozzáadnak egy élt az első argumentumukhoz, amely a második argumentumra mutat.
Példa: Legyen adott a következő átírási szabály:
Tekintsük a következő kifejezést:
Az átírás végeredménye:
A kék részek az átírás előttiek, a zöld részek az átírás utániak, a piros részek az alkalmazás-csúcs eliminálása után keletkeztek.
Ha nem tudjuk átírni az adatgráfot a gyökérnél, akkor a gráf gyökér normál formában van (ennek az analógja a fej normál forma a termátíró rendszerekben).
Ha nem tudjuk átírni az adatgráf egyetlen részgráfját sem (nincs redex), akkor a gráf normál formában van.
Ha a gráfban több redex is van, akkor dönthetünk, hogy melyiket írjuk át. A konfluens gráfátíró rendszereknél a döntés nem befolyásolja a végeredményt, de befolyásolhatja a végeredményhez vezető lépések számát.
Különböző redukciós stratégiákat dolgoztak ki, amelyek megmondják hogy egy adott adatgráf és adott gráfátírási szabályok esetén melyik szabályt kell alkalmazni, és hol kell a szabályt alkalmazni.
A redukciós stratégia normalizáló, ha minden adatgráfot ami normál formára hozható, normál formára is hoz.
Folytatás következik