Iti0210lab8

Allikas: Lambda
Redaktsioon seisuga 28. märts 2018, kell 16:42 kasutajalt PriitJ (arutelu | kaastöö)
Mine navigeerimisribale Mine otsikasti

Teadmusbaas Prologis

Sissejuhatus

Proovime Prologis teha teadmusbaasi, mis võimaldab teha järeldusi kollimaailma kohta. Kollimaailm on kirjeldatud õpikus [AIMA] 7.2.

Teadmusbaas peaks koosnema kahest osast:

  • reeglid, mis kirjeldavad, kuidas kollimaailm töötab
  • faktid, mis kirjeldavad, mida kollimaailmas tegutsev agent seni teinud ja kogenud on

Ehk siis vaatame mingit kindlat ajahetke nagu näiteks joonisel:

Teeme järeldusi selle ajahetkel kohta. Minimaalselt peaks saama järeldada, kas mingi ruut on turvaline, järgmine samm oleks järeldada, mis on õige järgmine käik.

Abiks

  • http://learnprolognow.org
  • Adventure in Prolog
  • Lihtsaid katsetusi saab teha veebis, ilma midagi installimata: SWISH.
  • Arvutiklassis on olemas SWI-Prolog. Linuxi jaoks soovitaks GNU Prologi, mis on antud ülesande jaoks täiesti piisav.
  • SWI-Prologis pane faililaiendiks ".pl". Siis käsitleb SWI-Prolog seda kui Prologi koodi, muidu võivad menüüdest vajalikud asjad puudu olla.

Mõned lihtsamad reeglid:

 person(sokrates).              % Sokrates on inimene
 mortal(X):-                    % kõik inimesed on surelikud
   person(X).
 mortal(X):-                    % teine reegel surelikkuse kohta. Prolog vaatab ükshaaval kõiki reegleid
   dead(X).
 dead(platon).                  % veel üks fakt

Kui me teeme päringu ?- mortal(platon). või ?- mortal(sokrates)., siis mõlemale saame vastuseks "yes" (või midagi analoogset, sõltuvalt Prologi versioonist). ?- dead(sokrates). annab aga vastuse "no", kuna selle kohta otsest fakti, ega ka sobivat reeglit ei eksisteeri.

Rekursiivne reegel:

 % faktid
 parent(john,paul).             % paul on johni vanem  
 parent(paul,tom).              % tom on pauli vanem
 parent(tom,mary).              % mary on tomi vanem
   
 % reegel: esivanema leidmine
 ancestor(X,Y):-  % baasjuhtum. Kui Y on X otsene vanem, siis ta ongi kohe esivanem
   parent(X,Y).   
 ancestor(X,Y):-  % rekursiivne reegel
   parent(X,Z),   % Valime mingi Z-i, kes on X-i vanem ja vaatame, kas temale
   ancestor(Z,Y). % on võimalik rekursiivselt esivanemat leida. Z esivanem on ka X esivanem.

Prologis on küll sisse ehitatud debugger, aga esialgu võib vigade otsimise jaoks lihtsam olla teksti välja trükkida. See näeb välja protseduurilise programmeerimise moodi, aga write/1 ja nl/0 on lihtsalt loogikapredikaadid, mille väärtus on alati true.

 mortal(X):-
   write('kas X on inimene?'), nl,
   person(X).
 mortal(X):-
   write('kas X on surnud?'), nl,
   dead(X).

Cut ("!") on kasulik, kui tahetakse otsingut varakult lõpetada. Näiteks, kui ma teen eelnevate näidete puhul päringu ?- mortal(X)., pakutakse mulle nii Sokratest, kui Platonit. Kui ma tean ette, et mind huvitab ainult üks vastus, siis ma võin teha nii:

 existmortal(X):-
   mortal(X),
   !.                        % Kui Prolog ühe sobiva X väärtuse on leidnud ja siia kohta jõudnud
                             % siis ta rohkem enam ei proovi.

Eituse jaoks on sümbol "\+" (siin on pikem seletus):

 deceased(X):-
   mortal(X),
   \+ alive(X).
 mortal(mccartney).
 mortal(lennon).
 alive(mccartney).

Kui mingit asja tahetakse dünaamiliselt modifitseerida, siis tuleb seda Prologile ette deklareerida.

 :- dynamic(person/1).

Peale seda query ?- retract(person(sokrates)). õnnestub.

Kui mingi predikaat on dünaamiline, siis saab selle kohta ka reegleid teha, ilma et predikaat ise oleks defineeritud. Alljärgnev on OK, isegi kui breeze fakte Prologi andmebaasis ei leidu:

 :- dynamic(breeze/1).
 agentpos(1, 1, 1).  % agent asus ajahetkel 1 kohal 1, 1
 breezy(X, Y):-        % koordinaat X, Y on tuuline, kui
   agentpos(X, Y, T),  % agent on kohal X, Y ajahetkel T ja
   breeze(T).          % agent tajub tuuletõmmet ajahetkel T

Alakriips on anonüümne muutuja (meid ei huvita, mis ta väärtus on):

 visited(X, Y) :-
   agentpos(X, Y, _). % agendi positsioon mingil suvalisel ajahetkel oli X, Y

Tuleb tähelepanelik olla, mis on tundmatud muutujad ja mis on teadmusbaasist tuletatavad.

 nopit(X, Y) :-              % saab küsida nopit(X, Y).
   visited(L, Y),
   X is L + 1,               % kui X on tundmatu, siis arvutatakse ta visited(L, Y) abil leitud L-ist
   \+ breezy(L, Y).
 
 nopit_katki(X, Y) :-        % nopit_katki(X, Y) annab vea. Suudab ainult kontollida väiteid nagu nopit_katki(1,2).
   visited(L, Y),
   L is X - 1,               % kui X on tundmatu, siis Prolog L-i väärtust kontrollida ei oska :-(
   \+ breezy(L, Y).