A Prolog programozási nyelv

Nyelvi elemek

Prologban a megjegyzések % jelel kezdõdnek és a sor végéig tartanak.

Termek

term ::= változó | konstans | funktor ( argumentumok )
változó ::= nagybetűalfanum...| _alfanum
alfanum ::= kisbetű | nagybetű | számjegy | _ konstans ::= atom | szám
funktor ::= atom
argumentumok ::= kifejezés | kifejezés , argumentumok
speciális-karakterek ::= + | - | * | / | \ | $ | ^ | < | > | = | ' | ~ | : | . | ? | @ | # | &

A Prolog nyelv elemi objektumai a termek. Ezek azok az objektumok, amelyekről bizonyos állításokat megfogalmazunk, és amelyekre a lekérdezésben megfogalmazott állítás vonatkozik. Termnek nevezzük tehát a konstansokat, a változókat és az összetett termeket.

A konstansok közé tartoznak a numerikus konstansok (természetes illetve lebegőpontos), a sztringkonstansok, továbbá az úgynevezett atomok. A természetes szám lehet decimális, bináris, oktális vagy hexadecimális - az utóbbiakat rendre a számjegyek előtti 0b, 0o illetve 0x jelöli -, vagy pedig karakterkonstans, 0'c alakban (ahol c egy karakter vagy egy escape szekvencia a C nyelvbelivel azonos formában).

A lebegőpontos szám kötelezően tartalmazza a tizedespontot, annak mindkét oldalán legalább egy számjegyet, és opcionálisan az exponenst. Az atomok azok az absztrakt objektumok, amelyeknek nincs tovább bontható belső struktúrájuk (nyilván innen az elnevezés is). Az atomok azok a termek, amelyek segítségével modellezni tudjuk egy konkrét probléma "résztvevő" elemeit. Szerepüket tekintve leginkább a természetes nyelvekben megtalálható főnevekhez hasonlíthatóak. Az atomok elnevezésére tetszőleges kisbetűvel kezdődő alfanumerikus sorozatot használhatunk.

A változók itt logikai változó értelemben szerepelnek. Szemben a hagyományos programozási nyelvekkel most a változó nem egy írható/olvasható memóriahelyet jelöl, hanem egy adat-objektum "lokális nevét". A változók neve tetszőleges alfanumerikus karaktersorozat lehet (ideértve az _ karaktert is), melyek vagy nagybetűvel, vagy az _ karakterrel kezdődnek. Ha egy változóra csak egyszer hivatkozunk egy klózon belül, akkor ezt nem kell megnevezni, és névtelen (anonymous) változó gyanánt írjuk le, melyet az aláhúzás (_) karakter jelöl. Egy klóz számos névtelen változót tartalmazhat, melyeket egyedi változók gyanánt kezel a nyelv.

Megengedett változónevek:

A Prolog lehetőséget biztosít az atomoknál bonyolultabb felépítésű objektumok létrehozására is, amelyeket struktúráknak neveznek. Egy ilyen struktúra szintaktikai szempontból egy összetett termet jelent. Az összetett term egy funktorból és további termek sorozatából áll, amelyeket a term argumentumainak hívnak.

Példák

-12.345 % numerikus konstans _fakt % névkonstans "Gesztenye Guszti" % ez így egy sztringkonstans gesztenye_guszti % így egy atom GG % ez meg már változó szemelyi_adatok("Szigor Igor", 30, ferfi) % ez egy összetett term, ami % felfogható egy rekordnak is

(Rekord: A rekord-deklaráció formája rekordnév(tag1, ..., tagn). Például egy bináris fát megadhatunk az alábbi formában: bintree(1, bintree(2, bintree(3, ures, ures), bintree(4, ures, ures)), bintree(5, ures, ures)).)

Ez utóbbihoz hasonló struktúra megtalálható szinte minden programozási nyelvben, de a Prolog-beli összetett termeknek van egy extra tulajdonságuk is. Nevezetesen ha a term egyes argumentumai logikai változók, akkor az nem egy hanem több (valamilyen szempontból azonos tulajdonságú) objektumot jelöl.

A termek közé tartozik tehát az összes aritmetikai kifejezés is. Ilyen aritmetikai kifejezések létrehozhatóak az előre beépített műveletek (funktorok) segítségével, vagyis a +, -, *, / stb. felhasználásával. Ezeket a funktorokat használhatjuk mind prefix mind pedig infix módon is.

Egy program szerkezete

Egy Prolog-program nem más mint állítások (vagyis mondatok) sorozata. A Prolog állításoknak több fajtájuk létezik. A legegyszerűbb állítások a relációs adatbázistáblák sorainak felelnek meg, ezek a tényállítások, ezek feltétlenül igaz állítások. A tényállásokon kívül léteznek bonyolultabb állítások, ezeket szabályoknak hívjuk. Egy szabály két részből áll: egy fejből és egy törzsből. Ezek közül szintaktikailag nem kötelező mindkettő használata (de legalább az egyiket kell), ekkor a következő további elnevezések használatosak:

Formálisan:

program ::= predikátum
predikátum ::= klód állítás ::= tény | klóz | direktíva
tény ::= fej .
szabály ::= fej :- törzs .
direktíva ::= parancs | lekérdezés
parancs ::= :- törzs
lekérdezés ::= ?- törzs
fej ::= kifejezés
törzs ::= cél
cél ::= kifejezés

Kicsit más megvilágításba helyezve a dolgot, egy Prolog-program tekinthető valamiféle egyszerű adatbázisnak is. Az adatok egy része közvetlenül kerül tárolásra "tényállításként" (egységklóz), a többi pedig kinyerhető a következtetési szabályok (klózok) segítségével. Emiatt szokás a Prolog-programokat Prolog-adatbázisként is emlegetni. Ezt az adatbázist a programozó hozza létre, és a program végrehajtása közben kerül további feldolgozásra az interpreter v. compiler által.

Egy Prolog-program végrehajtása a lekérdezésben megfogalmazott állítás teljesülésének az eldöntését jelenti, amit a programban található egyszerű állítások és a levezetési szabályok felhasználásával lehet elvégezni. Ezeket a tényállításokat ill. levezetési szabályokat nevezzük célnak. Minden egyéb procedurális jellegű művelet (pl. input és output) rekurzió segítségével valósítható meg, vagy pedig egy bizonyos előredefiniált (beépített) részcélra való hivatkozással (természetesen a levezetési szabály törzsében). Az ilyen részcélok teljesítése a program többi részével szemben nem lehet mellékhatásoktól mentes, gondoljunk csak a fájlkezelésre vagy a képernyőre való kiíratásra.

Operátorok

Az operátorok a prologban a szintetikus édesítőszerek közé tartoznak: olyan egy- vagy kétargumentumú struktúrák, amelyek infix jelöléssel is írhatóak.Az operátorokat a rendszer mindig szabványos alakra transzformálja, és csak utána végzi el az illesztést. (Így például a+b+c egyesíthető X+c -vel, de nem egyesíthető a+X-szel, mert szabványos alakja +(+(a, b), c.) A programozó maga is definiálhat új operátort, ennek formája:

:- op(prioritás, fajta, operátornév).

Az operátornév tetszőleges névkonstans lehet. A prioritás egy 1 és 1200 közötti szám, a fajta pedig az azonos prioritású operátorok közötti zárójelezés sorrendjét jelölő sztringkonstans: yfx jelzi a balról jobbra kötő operátort, xfy a balról jobbra kötőt, az xfx operátor egyik oldalán sem használható azonos prioritású operátor zárójelezés nélkül. Hasonlóan, az egyoperandusú operátorok yf, fy, xf vagy fx típusúak lehetnek. A Prolog beépített szabványos operátorainak listája:

Mivel a mondatokban használatos összekötő jelek is szabványos operátorok, így a mondat egyetlen kifejezésként is felírható, ami lehetőséget ad a meta-programozásra: pl. dinamikus predikátumokat hozhatunk létre, amiket futási időben módosítunk.

Az operátorok felhasználása

A Prolog általános operátorfogalma többféle módon is megkönnyíti a programfejlesztési munkát. Először is az operátorfogalom teszi lehetővé, hogy az aritmetikai beépített eljárásokban a megszokotthoz hasonló módon írjuk le a számításainkat, pl.

X is N*8 + A mod 8

Másodszor, az operátorfogalom teszi azt is lehetővé, hogy a Prolog szabályokat, vezérlési szerkezeteket le tudjuk írni mint Prolog-kifejezéseket. Ez a homogén szintaxis nemcsak a rendszer megvalósítóinak könnyíti, de számos ún. meta-programozási lehetőségre ad módot. Például lehetőség van ún. dinamikus predikátumok létrehozására, amelyekhez futási időben adhatunk hozzá új klózokat, pl.

...,asserta((p(X):-q(X),r(X))),...

Harmadszor, operátorok segítségével a Prolog programok természetesebbé, olvashatóbbá tehetők. (fontos értenünk, hogy ez csak számunkra olvashatóbb alak, a Prolog rendszer úgyis szabványos alakra hoz mindent). Negyedszer, operátorokat használhatunkaz adatok természetesebb formában való leírására is. Például, ha a '.' jelet operátornak deklaráljuk, akkor kémiai vegyületek leírására a szakmai nyelvhez közel álló jelölést használhatunk:

:-op(100,xfx,[.]). sav(kén,h.2-s-o.4).

Végezetül az operátorok teszik lehetővé a "klasszikus" szimbolikus kifejezésfeldolgozást, például a szimbolikus deriválást. Rossz tulajdonságai is vannak az operátoroknak. Az operátorok nem lokálisak az adott Prolog modulra nézve, ezért egy nagyobb projektben gondot jelenthetnek más emberek által megírt vagy átdefiniált operátorok számunkra (például tudtunk nélkül egyik napról a másikra egy általunk használt operátor prioritását valaki más megváltoztathatja).

Példa

Ezek után lássunk egy példát egy Prolog programra:

sum_tree(leaf(Value),Value). sum_tree(node(Left,Right),S) :- sum_tree(Left,S1), sum_tree(Right,S2), S is S1 + S2.

A fenti Prolog kód egy predikátumot definiál, ez a sum_tree predikátum. Ez a predikátum egy relációt ír le egy bináris fa és egy érték között, a fához a leveleinek összegét rendeli. A fenti predikátumot két klózzal írtuk le. Az első klóz egy tényállítás (csak fejből áll). Azt fejezi ki, hogy egy egyetlen levélből álló fa levélösszege maga a levél értéke. A tényállításokat feltétel nélkül igaznak tekintjük. A második klóz egy szabály. Minden klózt egy ponttal zártuk le a nyelv szintaxisának megfelelően. Ha egy predikátumot több klózzal írunk le, akkor a klózoknak meg kell egyezniük a nevükben és aritásukban.

A sum_tree(node(Left,Right),S) egy összetett kifejezés. Struktúraneve sum_tree, argumentumlistája két argumentumot tartalmaz, az egyik a node(Left,Right), amely szintén egy összetett kifejezés, a másik az S változó. De mi van a következõ kifejezéssel S is S1 + S2 ? Ez egy ún. operátoros kifejezés, melynek belső kanonikus alakja: is(S , +( S1 , S2 )) . Tehát neve is/2, és második argumentuma szintén egy operátoros kifejezés a +/2 funktorral.

Operátoros változat

Ebben a szakaszban bemutatjuk az operátorok alkalmazását a jól ismert binárisfa-példánk egyszerűbb, nem-megkülönböztetett uniót használó változatában. Definiálunk egy -- nevű xfx típusú operátort:

:-op(500,xfx,--).

Ez a "--" név fogja helyettesíteni az eddigi node struktúranevet. Mivel azonban a "--" operátort ínfixnek deklaráltuk, sokkal kényelmesebben írhatunk le egy fát:

5--(3--2)

Ez megfelel a

--(5,--(3,2))

szabványos alaknak, amikoris ha -- helyére node-ot írunk, visszajutunk a régebbi alakra. Az operátorral tehát csak annyit nyertünk, hogy könnyebben olvashatóbbá tettük a fánk leírását, valamint a kódunkat is:

sum_tree3(Left--Right,S):- sum_tree3(Left,S1), sum_tree3(Right,S2), S is S1 + S2. sum_tree3(Tree,S):- integer(Tree), S = Tree.