Prologi loogikaoperaatorid

Eesmärgi tõestamist, s.t. programmipredikaatide käsitlemise järjekorda juhivad loogikaoperaatorid "ja" (konjunktsioon, Prologi kirjaviisis märgib seda koma , või sõna and), "või" (disjunktsioon, Prologi kirjaviisis ; või or) ja eitus (Prologis sõna not või \+).

Predikaadide kirjeldamisel (s.t. predikaadi kehas, märgist :- paremal) on kõik laused tavaliselt seotud konjunktsioonidega (komad); disjunktsioon ("või") on vaid teksti lühendamiseks ja selle võib alati asendada lausete lisamisega predikaatidele, näiteks predikaat

laps(Vanem,Laps):-
poeg(Vanem,Laps);tütar(Vanem, Laps).

(s.t. Laps on Vanema poeg või tütar) asemel võib kasutada

laps(Vanem,Laps):-
poeg(Vanem,Laps).
laps(Vanem,Laps):-
tütar(Vanem, Laps).

seega ";" on vaid lühend, millega saab predikaate ökonoomsemalt kirjeldada. "Või" kasutamisel tekib mõnikord probleeme skoobiga, eriti kui predikaadi paremal pool on järjest mitu komade-semikoolonitega seotud lauset - millised laused on seotud "ja"-ga (konjunktsioon), millised "või"-ga (disjunktsioon)? Loomulikult on nende kasutamine määratud prioriteetidega, kuid probleeme saab vältida ka sulgude kasutamisega (ja tekst muutub arusaadavamaks). Kui "hea_auto" on auto, mis on välja lastud käesoleval sajandil või mis on Mercedes, siis sellise predikaadi võib määrata ilma "või"-d kasutamata:

hea_auto(Auto):-
väljalaskeaasta(Auto,Aasta),
Aasta > 2000.
hea_auto(Auto):-
firma(Auto,mersu).

"Või" kasutamisel saab kirjelduse muuta kompaktsemaks, kuid predikaadi parema poole struktuur on parem näidata sulgude abil :

hea_auto(Auto):-
((väljalaskeaasta(Auto,Aasta), Aasta > 2000); firma(Auto,mersu)).

Prologi eitus not erineb veidi tavaloogika eitusest.
Selle kasutamisel peab alati jälgima, et eituse all olevatel muutujatel peab olema väärtus (selleks hetkeks, kui Prolog jõuab eituse all oleva predikaadi tõestamiseni). Kui muutuja mõni väärtus muudab predikaadi tõeseks ja mõni - vääraks, siis kuidas Prolog peab suutma otsustada, kas predikaat on tõene või väär ?
Samuti peab meeles pidama, et Prolog teab/tunneb vaid meie maailma väikest lõplikku osa - vaid seda, mida talle programmis on kirjeldatud. Kui näiteks tahetakse defineerida puhkepäev kui mitte-tööpäev:

tööpäev(esmaspäev).
tööpäev(teisipäev).
tööpäev(kolmapäev).
tööpäev(neljapäev).
tööpäev(reede).
puhkepäev(Päev):-
not(tööpäev(Päev)).
siis leiab Prolog päringu ?-tööpäev(Päev) järel küll kõik tööpäevad, kuid puhkepäevi ei leia:
?- puhkepäev(P).
no
- kust tema peab teadma, millised päevad veel üldse on olemas?

Probleemi vältimiseks peaks muutuja Päev olema enne predikaadi puhkepäev(Päev) kasutamist väärtustatud, näiteks nii:

päev(esmaspäev).
päev(teisipäev).
päev(kolmapäev).
päev(neljapäev).
päev(reede).
päev(laupäev).
päev(pühapäev).
puhkepäev(Päev):-
päev(Päev),
not(tööpäev(Päev)).

Kõige rohkem probleeme ja raskesti avastatavaid vigu põhjustab ! (cut, lõige). See muudab Prologi otsimisstrateegiat - katkestab tagurdamise (backtrackingu); sisuliselt õeldakse ! paigutamisega Prologile umbes nii: "Kui sa jõudsid siiani, siis pole (tõestatava predikaadi paremal pool) eelnenud predikaatidele teisi lahandeid otsida", seega sellega püütakse muuta programmi kiiremaks. Ka ! kasutamist saab alati vältida lausetele tingimuste lisamisega. Kui näiteks labürindis teed otsides tahetakse, et pärast käimata ruumi leidmist enam juba käidud ruume ei uurita, võib predikaadi otsi(Ruum) esimeses lauses kasutada lõiget:

otsi(c3):-
write('Väljas!').
otsi(Ruum):-
pääseb(Ruum, Ruum1),
not(clause(käidud(Ruum1), true)),!,
assert(käidud(Ruum)),
write('Ruumist '),
write(Ruum),
write(' lahen ruumi '),
write(Ruum1),
nl,
otsi(Ruum1).

Kuna vajaduse tagurdamiseks võib tekkida väga mitmesugustel tingimustel (selle võib tekitada iga predikaat, mis kasutab lõikega predikaati), on lõike kõrvalmõjusid väga raske ette näha ja selle pärast tekitabki lõike kasutamine sageli raskesti avastatavaid vigu.

Olgu näiteks nimistus elemendi esinemist kontrolliv predikaat member(Element, Nimistu) defineeritud lõike abil (kui Element on nimistu pea, pole rohkem enam tarvis otsida):

member(Element,[Element|_]):-!.
member(Element,[_|Keha]) :-
member(Element,Keha).

Selline definitsioon tundub täiesti loomulik ja peaks andma (veidi) kiirema predikaadi kui lõiget kasutamata. Kuid kui seda predikaati tahetakse kasutada nimistu elementide genereerimiseks, s.t. selle poole pöördumisel on Element muutuja ja muutujal Nimistu on väärtus ( sellist kasutamist on sageli tarvis), leiab Prolog vaid nimistu esimese elemendi:

?- member(X,[s,a,d]).

X = s
no

- ülejäänud elementide leidmiseks peab tagurdama, aga ! seda ei luba.


Ülesandeid:
1. Ylesande tekst


Küsimused, probleemid: ©2004 Jaak Henno