Prolog (programovací jazyk)

Z Multimediaexpo.cz

Prolog je logický programovací jazyk. Patří mezi tzv. deklarativní programovací jazyky, ve kterých programátor popisuje pouze cíl výpočtu, přičemž přesný postup, jakým se k výsledku program dostane, je ponechán na libovůli systému. Prolog se snaží o pokud možno abstraktní vyjádření faktů a logických vztahů mezi nimi s potlačením imperativní složky. Prolog je využíván především v oboru umělé inteligence a v počítačové lingvistice (obzvláště zpracování přirozeného jazyka, pro nějž byl původně navržen). Syntaxe jazyka je velice jednoduchá a snadno použitelná pravě proto, že byl původně určen pro počítačově nepříliš gramotné lingvisty. Prolog je založen na predikátové logice prvního řádu; konkrétně se omezuje na Hornovy klauzule. Běh programu je pak představován aplikací dokazovacích technik na zadané klauzule. Základními využívanými přístupy jsou unifikace, rekurze a backtracking.

Obsah

Historie

Jméno „Prolog“ bylo vybráno Philippem Rousselem jako zkratka z "PROgrammation en LOGique” (francouzsky 'programování v logice'). Byl vytvořen okolo roku 1972 Alainem Colmerauerem ve spolupráci s Philippem Rousselem, na základě procedurálního výkladu Hornovy klauzule od Roberta Kowalskiho. Prolog v sobě zahrnuje mnoho z automatických odvozujících systémů vyvíjených v letech 1960 až 1970. Přímým předchůdcem Prologu jsou Q-systémy.

Prolog je příklad programovacího jazyka čtvrté generace. Japonský Projekt počítačů páté generace, který začal v roce 1981, si ho vybral za vývojový jazyk a tím zaměřil značnou pozornost na tento jazyk a jeho schopnosti. Od těch dob se Prolog rozvětvil do mnoha oborů, přičemž se vyvíjel jako nové logické jazyky. Mezi těmito druhy Prologu jsou určité rozdíly, část v sémantice, část v syntaxi. Přesto nebývá problém přeorientovat se z jednoho na druhý.

Datové typy

Jednotnou datovou strukturou, se kterou Prolog pracuje, je tzv. term - pojem převzatý z formální logiky. Základní členění termů:

  • term
    • struktura
    • jednoduchý term
      • proměnná
      • konstanta
        • číslo
        • atom

Atomy

Atomy lze dle konstrukce rozdělit do třech kategorií:

  • řetězce znaků začínající malým písmenem obsahující pouze písmena, číslice a podtržítko - např: otec franta novy_Clen2
  • posloupnost znaků uzavřená v apostrofech (některé implementace používají uvozovky) - např: 'Pepa 26.6.2007' 'velký les'
  • atomy skládající se pouze ze speciálních znaků - např: , ; <*!*> :-)

Čísla

Původní Prolog podporoval pouze celá čísla. Řada implementací pracuje s reálnými i racionálními čísly a s neohraničenými celými čísly.

Například:

?- X is 2^200, Y is (3 rdiv 8) + (4 rdiv 9).
     X = 1606938044258990275541962092341162602522202993782792835301376
     Y = 59 rdiv 72

Proměnné

Proměnné začínají velkým písmenem nebo podtržítkem a nesmí obsahovat speciální znaky. Vyskytují se v pravidlech, kde popisují účastníky vztahu, nebo v dotazech, kde reprezentují hledané objekty. Rozsah platnosti proměnné je pouze jedna klauzule, stejnojmenná proměnná v sousední klauzuli nemá s touto nic společného, i když je třeba součásti stejného predikátu. Hodnotu získává pomocí srovnávání (unifikace) a po jejím přiřazení se již dále nemění, pokud se použité pravidlo, které ji přiřadilo, neodvolá (backtracking).

Z pohledu interpretu lze proměnné rozdělit na dva typy:

  • volné - jejich hodnota zatím není známá a interpret se ji snaží nalézt
  • vázané - z dřívějších kroků řešení již plyne její hodnota, tedy je s ní svázána

Příklad použití proměnných:

  • klauzule:
rodic(jana,petr).
rodic(otto,eva).
dite(X,Y) :- rodic(Y,X).
  • dotazy:
?- rodic(X,petr).
     X = jana ;
     No
?- dite(X,jana).
     X = petr ;
     No

Speciálním typem je tzv. anonymní proměnná. Značí se jako podtržítko a používá se v pravidlech. Její hodnota není podstatná a Prolog ji ve výsledcích nezobrazuje.

Příklad: predikát, zda X je dítě

je_dite(X) :- dite(X,_).

Struktury

Struktury jsou tvořeny z funktoru a argumentů. Počet argumentů udává aritu struktury. Některé operátory mohou být používány také v infixovém tvaru. Strukturou tedy mohou být i klauzule, kde se jako funktor používá infixový operátor :- .

Příklady:

muz(adam).                               %funktor muz má aritu 1
datum(27,6,2007).                        %funktor datum má aritu 3
okamzik(datum(27,6,2007),cas(13,54)).    %funktor okamzik má aritu 2
prarodic(X,Y) :- rodic(X,Z), rodic(Z,Y).

V jednom programu se mohou vyskytovat dva stejně pojmenované funktory, pokud mají různé arity. Speciálním případem struktur jsou seznamy a řetězce .

Seznamy

Seznamy jsou definovány induktivně: Prázdný seznam je označen atomem [ ] , k reprezentaci neprázdného seznamu slouží binární funktor tečka '.' . Neprázdný seznam je tedy tzv. tečkový pár (terminologie pochází z jazyka LISP) .(Hlava,Tělo), kde Hlava je první prvek seznamu a Tělo je seznam tvořený zbývajícími prvky seznamu. Pro zjednodušení zápisu lze použít výčet prvků v hranatých závorkách (oba zápisy jsou ekvivalentní).

Příklad:

.(a,.(b,.(c,[ ]))) .
[a,b,c] .

Pro práci se seznamy se často využívá operátor '|', který umožňuje přístup k jednotlivým částem seznamu. Seznam lze pak zapsat jako [Začátek|Tělo], kde Začátek je výčet (nikoliv seznam) prvků tvořící začátek definovaného seznamu a Tělo je seznam (nikoliv výčet) tvořící zbytek definovaného seznamu (je-li prázdný, nemusí se uvádět).

Častým zdrojem chyb je právě záměna operátoru '|' a čárky.

Řetězce

Řetězce jsou sekvence znaků uzavřené v uvozovkách, které jsou ekvivalentní seznamu (číselných) kódů jednotlivých znaků v místní znakové sadě nebo v Unicode, pokud systém podporuje Unicode.

Příklad:

?- Xs = "Πρόλογ".
     Xs = [928, 961, 972, 955, 959, 947]

Programování v Prologu

Programování v Prologu se výrazně liší od programování v běžných procedurálních jazycích jako například C. Program popisuje vztahy definované pomocí klauzulí. Čistý Prolog se omezuje na Hornovy klauzule tedy predikátovou logiku prvního řádu. Základem Prologu je databáze klauzulí, které lze dále rozdělit na fakta a pravidla, nad kterými je možno klást dotazy formou tvrzení, u kterých Prolog zhodnocuje jejich pravdivost (dokazatelnost z údajů obsažených v databázi). Nejjednoduššími klauzulemi jsou fakta, které pouze vypovídají o vlastnostech objektu nebo vztazích mezi objekty. Složitějšími klauzulemi jsou pravidla, které umožňují pomocí implikace odvozovat nová fakta. Zapisují se ve tvaru hlavička :- tělo, kde hlavička definuje odvozovaný fakt, tělo podmínky, za nichž je pravdivý, obsahuje jeden či více cílů. Pokud se interpretu podaří odvodit, že tělo je pravdivé, ověřil tím pravdivost hlavičky.


Například lze do databáze uložit fakt, že Monika je dívka:

dívka(monika).

Poté lze dokazatelnost tohoto faktu prověřit otázkou, na kterou Prolog odpoví yes (ano):

?- dívka(monika).
     yes.

Také se lze zeptat na všechny objekty, o kterých je známo, že jsou dívky (středníkem požadujeme další výsledky):

?- dívka(X).
     X = monika;
     no.

Pravidla (závislosti) se zapisují pomocí implikací, např.

syn(A,B) :- rodič(B,A), muž(A).

Tedy: pokud B je rodičem A a zároveň je A muž, pak A je synem B.

Predikát

Predikát lze charakterizovat jako sadu klauzulí se stejným jménem a stejnou aritou. Může obsahovat fakta i pravidla, které fungují jako alternativy – platnost predikátu lze dokázat libovolnou z nich. Pravdivost predikátu vyjadřují dvě logické konstanty – true, fail. Při vyhodnocování pravidel lze využít základní logické operátory:

  • konjunkce – čárka ',' – pokud některá část selže, další se nevyhodnocují
  • disjunkce – středník ';' – disjunkci lze také zapsat, tak že pravidlo rozepíšeme na více řádků, např:
raz(X) :- dva(X); tri(X).

je totéž co

raz(X) :- dva(X).
raz(X) :- tri(X).

Rekurze

Rekurze v Prologu nahrazuje cykly, tudíž je velmi často používána. Například predikát pro nalezení předka:

predek(X,Y) :- rodic(X,Y).
predek(X,Y) :- rodic(X,Z), predek(Z,Y).

Při používání rekurze je třeba dávat pozor na pořadí klauzulí, které Prolog prochází zleva doprava. Jejich prohození může vést ke snížení efektivity algoritmu nebo až k nekonečnému cyklu. Například prohození klauzulí ve výše uvedeném příkladu by mělo za následek nekonečný cyklus.

predek(X,Y) :- predek(X,Z), rodic(Z,Y).

Reverzibilita

Funkční programování může být považováno za podmnožinu logického programování, pokud lze funkce uvažovat jako zvláštní případ relací. Právě díky relační povaze vestavěných predikátů, je typicky možné jej používat v několika směrech. Například klasická funkce length, která se používá pro určení délky seznamu, dokáže stejně dobře generovat kostru seznamu požadované délky nebo dokonce kostry seznamu a jejich délky.

Příklad použití length pro generování seznamu (ukazatele na vnitřní proměnné si Prolog značí ve tvaru _Gčíslo)

?- length(Ls, L).
     Ls = [],
     L = 0 ;

     Ls = [_G358],
     L = 1 ;

     Ls = [_G358, _G361],
     L = 2

Obdobně lze použít append jednak pro spojení dvou seznamů:

?- append([a,b,c], [d,e], Ls).
     Ls = [a,b,c,d,e]

stejně tak pro rozdělení jednoho seznamu na dva:

?- length(Xs, 2), append(Xs, Ys, [a,b,c,d,e]).
     Xs = [a,b]
     Ys = [c,d,e]

Z toho důvodu poměrně malá knihovna predikátů vystačí na mnoho programů. Další způsob využití predikátů je testování pravdivosti, pokud jsou známy všechny argumenty. Predikát vrací hodnotu Yes, když vztah platí. V ostatních případech No.

?- append([x], [], [x]).
    Yes

?- append([x], [y], [x]).
    No

Operátory

Operátor je z pohledu Prologu pouze jiný způsob zápisu struktury. Strukturu s funktorem + (běžné sčítání) bychom měli správně zapisovat takto jako strukturu +(A,B). Prolog nám však povoluje i tento infixový zápis A+B.

Tabulka standardních operátorů
operátor význam
 :- definice pravidla
 ?- otázka
 ; logické nebo
, logické a
= porovnání
\= opak =
is vyčíslení
<, >, =<, >= porovnání
==, \== porovnání bez přiřazení
=:=, =\= porovnání s vyhodnocením
+, -, *, /, mod, div standardní matematické funkce
not, \+ negace

Kromě těchto vestavěných operátorů si lze vytvořit libovolné vlastní operátory. Tyto operátory definujeme následujícím způsobem: :-op(priorita, specifikator, jmeno). Priorita je číselná hodnota, která udává, v jakém pořadí se mají operátory vyhodnocovat, jestliže výraz obsahuje více operátorů. Vyšší číslo znamená nižší prioritu vyhodnocení. Pokud výraz obsahuje operátory se stejnou prioritou, musí program vědět, jakým směrem má při výpočtu postupovat. Proto se v operátoru definuje specifikátor. Specifikátory rozdělujeme podle směru vyhodnocení a postavení vzhledem k operandu následujícím způsobem:

  • Infixový binární operátor:
    • yfx směr vyhodnocení zleva; příkladem použití jsou aritmetické operátory
    • xfy směr vyhodnocení zprava; příkladem je operátor is
    • xfx nemá smysl hovořit o směru vyhodnocení, protože nalevo i napravo mohou být pouze operátory s vyšší vyhodnocovací prioritou; příkladem použití jsou porovnání.
  • Prefixový unární operátor:
    • fx - operátor stojí před operandem; příkladem je operátor not.
  • Lze definovat i postfixový unární operátor:
    • xf - operátor stojí za operandem, ale žádný standardní operátor tento specifikátor nepoužívá.

Standardně definované operátory:

:-op(1200, xfx, ':-').
:-op(1200, fx,[ :-, ?-]).
:-op(1100, xfy, ';').
:-op(1000, xfy, ',').
:-op(700, xfx, [ =, \=, is, <, >, =<, >=, ==, =\=, \==, =:=]).
:-op(500, yfx, [ +, -]).
:-op(500, fx, [ +, -, not]).
:-op(400, yfx, [ *, /, div]).
:-op(300, xfx, mod).


Negace - not

Negace v Prologu je chápána trochu jinak než v hovoru. V běžné řeči, a především v klasické logice, jsou výrok A i jeho negace not A výroky o témže světě. Výroky "je hezká" a "není hezká" jsou hodnocením nějakého objektu. Naproti tomu v Prologu

jehezka(X) 	%je konstatováním vlastnosti objektu X,

zatímco jeho negace

not jehezka(X) 	%znamená jen, že z programu nelze odvodit platnost predikátu jehezka(X).

Je tedy negace výrok o programu, ne o objektu X, ten (ta) X může být hezká - ba překrásná, ale někdo to zapomněl zanést do programu - a už platí not hezka(X), a pro program X hezká není. S negací ve smyslu Prologu se setkáváme v soudnictví: Komu jsme nedokázali, že vrah je, ten vrahem (ve smyslu práva) není.

Porovnání - =

Operátor = uspěje, pokud se oba argumenty povede unifikovat. Porovnání může nabývat čtyř různých významů:

  • Pokud je jedna proměnná specifikovaná a druhá nespecifikovaná, stanou se obě proměnné specifikované stejnou hodnotou.
  • Pokud jsou obě proměnné specifikované, porovnají se. Přitom integrity a atomy jsou rovny pouze samy se sebou.
  • Jsou-li obě proměnné nespecifikovány, stanou se sdílenými. Stane-li se později jedna z nich specifikovanou, bude stejnou hodnotou specifikována i druhá proměnná.
  • Pokud se jedná o 2 struktury, pak jsou si rovny, jestliže mají stejný funktor, souhlasí počet komponent a korespondující komponenty jsou si rovny.

Vyčíslení - is

Operátor is zapisuje ve tvaru Číslo is Výraz a vydá true, pokud je Číslo (zpravidla volná proměnná) úspěšně unifikováno s výsledkem Výrazu. Všechny proměnné, které se (případně) vyskytují ve Výrazu, musí být v okamžiku splňování cíle konkretizovány na číselnou hodnotu, jinak dojde k běhové chybě. Obdobně dojde k běhové chybě, pokud je proměnná Číslo již konkretizována na nečíselnou hodnotu. Ukázka funkce operátoru porovnání a vyčíslení na predikátu pro výpočet obsahu kružnice:

obsah_kruhu(Polomer,Obsah) :- Obsah is 3.14 * Polomer * Polomer.
obsah_kruhu2(Polomer,Obsah) :- Obsah = 3.14 * Polomer * Polomer.
?- obsah_kruhu(5,Y).
     Y = 78.5
     Yes
?- obsah_kruhu2(5,Y).
     Y = 3.14*5*5
     Yes

Příklady použití operátorů

?- 2+5 = 5+2.
     no                % tyto dva termy nelze unifikovat
?- 2+5 =:= 5+2.
     yes.              % oba aritmetické výrazy mají stejnou hodnotu
?- X is Y+2.           % běhová chyba - nedefinovaný aritmetický výraz
?- X<3.                % běhová chyba - výraz vlevo nelze vyčíslit
?- X is 2+3, Y is 2*X , Z is 12-3, Y is Z .
     no                % za jednotlivé proměnné se postupně substituovala čísla  X=5 , Y=10 a Z=9,
                       % při vyhodnocování predikátu Y is Z byla tedy proměnná Y již konkretizována a
                       % její hodnota různá od hodnoty proměnné Z
?- X is 7 , X is X+1 .
    no                 % za X substituovalo 7, což není rovno 6

Vstupy a výstupy

Pro pohodlnější práci nabízejí konkrétní implementace Prologu řadu předdefinovaných predikátů, které nazýváme vestavěné predikáty. Jako každý plnoprávný programovací jazyk nabízí i Prolog predikáty pro klasický vstup ze souboru a výstup do souboru. Tyto predikáty už nemají relační význam a přesahují čistě logickou podmnožinu jazyka. Pravdivostní hodnota některých predikátů pro vstup a výstup je vždy true, proto se často používají v kombinaci s predikátem fail.

Příklad notoricky známého programu „Ahoj světe“:

?- write('Ahoj světe').
     Ahoj světe
     Yes

V různých implementacích Prologu jsou i různé vestavěné predikáty. Proto by si každý uživatel měl zjistit, co všechno nabízí implementace, kterou používá. Existují však vestavěné predikáty, které jsou považovány za standardní a jsou obsaženy v (téměř) každé implementaci.

  • Ke změně aktuálního vstupu a výstupu slouží predikáty:
    • see(F) -nastaví aktuální vstup ze souboru F
    • seen(F) -uzavře vstupní soubor F a obnoví aktuální vstup z klávesnice ( jako by zavolal see(user) )
    • seeing(-F) -dotaz na aktuální vstupní soubor
    • tell(F) -nastaví aktuální výstup do souboru F
    • told(F) -uzavře výstupní soubor F a obnoví aktuální výstup na obrazovku ( jako by zavolal tell(user) )
    • telling(-F) -dotaz na aktuální výstupní soubor
  • Základním typem vstupu (resp. výstupu) v Prologu je vstup (resp. výstup) termů. Slouží k němu predikáty:
    • read(?T) -přečte z aktuálního vstupu jeden term (ukončený tečkou) a unifikuje jej s argumentem T
    • write(+T) -vystoupí instance termu T s právě platnými hodnotami substitucí za proměnné v termu T obsažené
  • Dalším typem je vstup a výstup znaků. Slouží k němu např. tyto predikáty:
    • get0(?Z) -přečte jeden znak z aktuálního vstupu, uspěje pokud jej lze unifikovat s termem Z
    • get(Z) -chová se stejně, pouze ignoruje (přeskakuje), řídicí znaky (ASCII < 32)
    • put(Z) -výstup znaku do aktuálního výstupu (ne řídicí znaky)
    • tab(N) -vystoupí N mezer (N přirozené číslo)
    • nl -přechod na novou řádku ( pascalské writeln ) .

Vyhodnocování

Mějme databázi:

muj_pritel(petr).
muj_pritel(marek).
muj_pritel(Pritel) :- markuv_pritel(Pritel),petruv_pritel(Pritel).
markuv_pritel(lenka).
markuv_pritel(jirka).
petruv_pritel(jirka).
petruv_pritel(petra).

Na otázku muj_pritel (Kdo). získáme následující odpovědi:

     Kdo = petr ;
     Kdo = marek ;
     Kdo = jirka ;
     no

Při vyhodnocování se vždy prohledává databáze od shora dolů a na hladinách, kde existují alternativy, se pak pokračuje zleva doprava. O splnění cílů rozhoduje tzv. srovnání (unifikace). Srovná se vždy aktuální cíl s faktem či hlavou pravidla v databázi. Součástí úspěšného srovnání je i případný vznik vazby proměnné na hodnotu. Pokud nedojde k splnění cíle, může Prologovský program použít tzv. navracení (backtracking), při kterém dojde ke zrušení vazeb proměnných na hodnoty a hledání jiného řešení. Po nalezení řešení může navracení vyvolat i uživatel a to stisknutím klávesy středník.

V našem případě se tedy nejprve Prolog pokusí porovnat muj_pritel(Kdo) a muj_pritel(petr). Proměnná Kdo byla nespecifikována, a tak mohla vzniknout vazba na atom petr. Cíl je splněn, Prolog vypíše první odpověď. Pokud máme zájem o vyhledání alternativního řešení, požádáme Prolog o znovusplnění cíle. Tedy použijeme klávesu středník. Prolog zruší vazbu na atom petr, označí si již splněný fakt a pokouší se najít další řešení. Protože databáze se prohledává směrem dolů, dalším porovnáním bude muj_pritel(Kdo) a muj_pritel(marek). Vzniká nová vazba proměnné Kdo a to na atom marek. Cíl je opět splněn, vypíše se druhá odpověď. Po stisku klávesy středník dojde ke zrušení vazby proměnné Kdo na atom marek, označení použitého faktu a hledání dalšího cíle. Při hledání další odpovědi pracuje Prolog s pravidlem a postupuje následovně:

  • Nespecifikované proměnné Kdo a Pritel se stanou sdílenými, tzn. obě proměnné mohou mít vazbu na jedinou hodnotu.
  • Vyhledá se první Markův přítel, což je v našem případě Lenka a na proměnné Kdo i Pritel se naváže hodnota lenka.
  • Poté hledá predikát petruv_pritel(lenka), toto hledání je však neúspěšné.
  • Proto se použije navrácení.
  • Prolog se vrátí k predikátu markuv_pritel(Pritel), zruší vazbu na proměnnou lenka a snaží se najít jinou vazbu, což v našem případě bude atom jirka.
  • Nyní tedy program hledá vazbu na predikát petruv_pritel(jirka), tento predikát najde, čímž splní cíl a je vypsána odpověď Jirka.

Další hledání probíhá podobně a je neúspěšné. Proto je poslední odpověď no. Z výše napsaného vyplývá, že Prolog prohledává strom programu (derivační strom) shora dolů a na hladinách. Tam, kde existují alternativy, pak zleva doprava.

Strategii vyhodnocování můžeme pozměnit pomocí některých vestavěných predikátů:

Selhání - fail

Jedná se o vestavěný predikát, který nemůže být nikdy splněn, tudíž vždy nutí program se vracet. Predikát fail se dá použít například v pravidlech, kde je třeba najít všechna řešení. Například:

muz(pavel).
muz(petr).
zena(petra).
vypis:-muz(X),write(X),nl,fail.

Pravidlo vypis vypíše všechny muže z databáze. Pokud bychom nepoužili predikát fail, vypsalo by se pouze první řešení. Tato technika se často označuje jako konstruktivní selhání.

Řez - !

Řez je vestavěný predikát, značený vykřičníkem '!' , který se používá, pokud chceme zakázat programu či uživateli, aby používal navracení. Často se používá u predikátu, které mají mít nanejvýše jedno řešení - tzv. determinovaný predikát. Řez si lze představit jako jednosměrku pro daný predikát. Pouze pokud se nepodaří najít řešení uvnitř řezu, je predikát opuštěn jako neúspěšný (pomocí portu fail) a řez je odvolán. Řez se používá ze dvou důvodů. Buď kvůli funkčnosti, nebo efektivnosti programu. Proto rozlišujeme dva základní druhy řezu. Zelený a červený řez. Zelený má vliv pouze na efektivnost programu, zatímco bez červeného by program správně nefungoval. Příklad použití červeného řezu:

max1(X,Y,X):-X>Y,!.
max1(X,Y,Y).

Příklad použití zeleného řezu:

max2(X,Y,X):-X>Y,!.
max2(X,Y,Y):-Y>=X.

Oba predikáty (max1 i max2) vyberou ze dvou čísel maximum. Predikát max2 by sice fungoval i bez řezu, ale pokud by bylo maximem X, dovolil by uživateli pomocí středníku návrat a hledání jiného řešení, které jistě neexistuje. Zde se tedy jedná o zelený řez. U predikátu max1 by mohla nastat podobná situace. Pokud by větší číslo bylo X, dovolil by uživateli návrat a zde by za maximum počítač označil chybně i Y. Predikát by tedy nefungoval správně, a proto se jedná o řez červený.

Řez se často využívá v kombinaci s predikátem selhání. Technika se označuje jako řez-selhání (cut-fail). Například máme-li k dispozici fakta muz(), lze snadno vytvořit predikát zena():

zena(X) :- muz(X), !, fail.
zena(X).

Pokud je X mužem, nemůže být ženou – řez zabrání hledání alternativ pro zena(X) a následné selhání určí výsledek.

Konvence a doporučení

Obecné doporučení:

  • používat mnemotechnické identifikátory
  • nezapomínat na dekompozici - složitě konstrukce rozdělit na více jednodušších
  • pokud se nelze vyhnout vedlejším efektům u predikátů, tak je alespoň důkladně okomentovat
  • dávat přednost negaci před řezem
  • rozepsání pravidla na více řádků je čitelnější než-li logická spojka (;)

Prolog používá obvykle dva typy komentářů:

  • /* komentář */
  •  % komentář až do konce řádku

Při komentování predikátu je vhodné (někdy i nutné) sdělit, jak se mají které argumenty použít. Využívá se proto všeobecně uznávaná konvence, která specifikuje argument umístěním jednoho z těchto tří znaků ( + - ?) před argument. Význam jednotlivých znaků:

- „výstupní argument“ - autor komentáře dává najevo, že v dotazech má na odpovídajícím místě stát nekonkretizovaná proměnná,
+ „vstupní“ parametr – autor komentáře dává najevo, že v dotazech má na odpovídajícím místě stát již konkretizovaný term,
? argument může být jak vstupní, tak i výstupní tj. v dotazech může na odpovídajícím místě stát „cokoli“ .

Příklad vzorově okomentovaného predikátu:

% predek(?Pred,?Pot) Pred je předkem potomka Pot
predek(Rod,Pot):- rodic(Rod,Pot).    % Rod je rodičem potomka Pot
predek(Pred,Pot):-
     rodic(Pred,MlPred),             % Pred je rodičem mladšího
     predek(MlPred,Pot).             % předka Mlpred potomka Pot


Externí odkazy