ČESKÁ ZEMĚDĚLSKÁ UNIVERZITA V PRAZE Provozně ekonomická fakulta
Seminární práce z Teorie ICT Implementace logické hádanky v Prologu
Autor :
Petr Pechek
1 Popis zvoleného problému Mým úkolem bylo vyřešit logickou hádanku pomocí programu prolog. Logická hádanka měla následující zadání: Skvělý fotbal Pět hráčů hraje v různých mužstvech na různých pozicích a má různě barevné dresy. 1. Hráč Carolina Panthers má fialový dres. 2. Samuel není zadní útočník, křídelní útočník hraje v mužstvu Dallas Cowboys. 3. Obránce má žlutý dres, Claude hraje za Dallas Cowboys. 4. Útočník nehraje za Green Bay Packers. 5. David nehraje za Oakland Raiders, Samuel je v černém. 6. Střední obránce je z Oakland Raiders, David hraje ve žlutém. 7. Victor je z Cleveland Browns a nenosí modrý dres. 8. Bill je útočník a hráči Cleveland Browns nosí červené dresy. Určete dres, družstvo a postavení každého hráče.
2 Stručný přehled teorie potřebné k vyřešení problému
2.1 Jazyk prolog 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. [1]
2.2 Datové typy v jazyku Jednotnou datovou strukturou, se kterou Prolog pracuje, je tzv. term - pojem převzatý z formální logiky. Základní členění termů: 2
•
term • •
struktura jednoduchý term • proměnná • konstanta • číslo • atom
2.2.1 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 • posloupnost znaků uzavřená v apostrofech (některé implementace používají uvozovky) • atomy skládající se pouze ze speciálních znaků [1]
2.2.2 Čí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.
2.2.3 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). [1] 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 dotazy
3
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.
2.2.4 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 :- . [1] 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. [1] 2.2.4.1 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čka-dvojice (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í). [1] 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). [1]
2.3 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. Pravidla (závislosti) se zapisují pomocí implikací.
4
2.3.1 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ů [1]
2.3.2 Rekurze Rekurze v Prologu nahrazuje cykly, tudíž je velmi často používána. Například predikát pro nalezení předka. [1] 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. [1]
3 Analýza problému Úkolem je zjistit jaký hráč hraje v jakém týmu, na jaké pozici v něm nastupuje a jaký nosí dres. Je dáno 8 podmínek, které musí být splněny, tedy musí platit zároveň. Jedné se konjunkci jednotlivých výroků. Řešení úlohy musí splňovat všechny tyto podmínky.
4 Rozbor možných řešení či přístupů k řešení Úloha může mít více, jedno nebo žádné řešení. Maximální počet řešení úlohy je 5 ! 4 . Tolik by bylo řešení pokud by úloha neměla omezující podmínky. Správného řešení lze dosáhnout pomocí postupného dosazování hodnot, tak aby splňovaly dané podmínky. Pokud již nelze dosadit další hodnotu vrátíme se zpět a zkusíme dosadit jinou hodnotu. Tento postup řešení se označuje jako prohledávání do hloubky.
5 Zdrojový kód programu jmeno(claude). jmeno(david). jmeno(viktor). jmeno(samuel).
5
jmeno(bill). druzstvo(green_bay_packers). druzstvo(dallas_cowboys). druzstvo(cleveland_browns). druzstvo(oakland_raiders). druzstvo(carolina_panthers). pozice(obrance). pozice(krajni_utocnik). pozice(zadni_utocnik). pozice(stredni_obrance). pozice(utocnik). dres(zluty). dres(modry). dres(cerveny). dres(cerny). dres(fialovy). %member(Y,[Y | _]). %member(Y,[_ | Remainder]) :- member(Y,Remainder). %zjisteni zda je Y prvkem struktury, neni nutne predikat je integrovany primo v prologu reseni(X):- % do proměné X uloží řešení (poradi prvku jmeno,druzstvo,pozice,dres) X=[
[_,_,_,zluty], %nutno zadat aby se ve vypisu neopakovali kombinace s prehozenymi prvky seznamu [_,_,_,modry], [_,_,_,cerveny], [_,_,_,cerny], [_,_,_,fialovy]],
member([_,green_bay_packers,_,_],X), %je zmineno, ze pouze hrac neni clenem klubu, proto_nutno dodefinovat klub member([_,_,zadni_utocnik,_],X), %stejne jako v predchozim pripade member([_,_,_,modry],X), %stejne jako v predchozim pripade member([_,carolina_panthers,_,fialovy],X), % podmínka 1 - Hrac carolina panthers ma fialovy dres member([samuel,_,Po,_],X),Po\==zadni_utocnik, % podminka 2 - Samuel neni utocnik member([_,dallas_cowboys,krajni_utocnik,_],X), % podminka 2 - za Dallas kowboys hraje křídelní útočník member([_,_,obrance,zluty],X), % podminka 3 - obrance ma zluty dres member([claude,dallas_cowboys,_,_],X), %podminka 3 - Claud hraje za Dallas Cowboys member([_,Klub,utocnik,_],X),Klub\==green_bay, %podminka 4 - Útočník nehraje za Green Bay Packers
6
member([david,Klub2,_,_],X),Klub2\==oakland_riders, %podminka 5 - David nehraje za Oakland Raiders member([samuel,_,_,cerny],X), %podminka 5 - Samuel je v černém member([_,oakland_riders,stredni_obrance,_],X), %podminka 6 - Střední obránce je z Oakland Raiders member([david,_,_,zluty],X), %podminka 6 - David hraje ve žlutém member([viktor,cleveland_browns,_,Dr],X),Dr\==modry, %podminka 7 - Victor je z Cleveland Browns a nenosí modrý dres member([bill,_,utocnik,_],X), %podminka 8 - Bill je útočník member([_,cleveland_browns,_,cerveny],X). %podminka 8 - hráči Cleveland Browns nosí červené dresy
6 Vysvětlení programu Program je realizován pomocí implementace prologu binprolog. Před spuštěním je nutné načíst program pomocí příkazu consult(fotbal). případně reconsult(fotbal). Předtím je potřeba zdrojový kód uložit do souboru fotbal.pl do adresáře s programem binprolog.
6.1 Jak program řeší jednotlivé úlohy Řešení programu se zobrazuje pomocí příkazu reseni(X). Jména hráčů, lze zobrazit pomocí příkazu jmena(X). Všechna družstva pomocí druzstvo(X). Všechny možné pozice pomocí pozice(X). A všechny barvy dresů pomocí dres(X). Pokud je vypnut interaktivní režim, tak dané příkazy zobrazí všechny možnosti.
6.2 Výpis trasování programu Výpis pomocí příkazu trace by měl rozsah na několik stovek stran. Zde je ukázka výpisu, která ukazuje jak program pracuje. Podtržítkem na začátku jsou označované anonymní proměnné. Jejich hodna nás nezajímá. Call: reseni(_2304) !!! dynamic(reseni/1) Call: _2304 = [[_2896,_2898,_2900,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny],[_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]] !!! compiled((=)/2) %vychozí řešení, dosazení výchozích hodnot do proměné Exit: [[_2896,_2898,_2900,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny],[_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]] = [[_2896,_2898,_2900,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny],[_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]] %volání zkončilo úspěšně Call: member([_2941,green_bay_packers,_2945,_2947],[[_2896,_2898,_2900,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny], [_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) %zjišťování zda může být [_2941,green_bay_packers,_2945,_2947] prvkem struktury, probíhá rekurzivně !!! dynamic(member/2) Exit: member([_2896,green_bay_packers,_2900,zluty],[[_2896,green_bay_packers,_2900,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny], [_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) %ukonceni zjišťování, přičlenění green_bay_packers do prního prvku struktury [_2896,green_bay_packers,_2900,zluty]
7
Call: member([_2950,_2952,zadni_utocnik,_2956],[[_2896,green_bay_packers,_2900,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny], [_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) !!! dynamic(member/2) Exit: member([_2896,green_bay_packers,zadni_utocnik,zluty],[[_2896,green_bay_packers,zadni_utocnik,zluty],[_2905,_2907,_2909,modry], [_2914,_2916,_2918,cerveny],[_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) Call: member([_2959,_2961,_2963,modry],[[_2896,green_bay_packers,zadni_utocnik,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny], [_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) !!! dynamic(member/2) Call: member([_2959,_2961,_2963,modry],[[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny],[_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) !!! dynamic(member/2) Exit: member([_2905,_2907,_2909,modry],[[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny],[_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) Exit: member([_2905,_2907,_2909,modry],[[_2896,green_bay_packers,zadni_utocnik,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny], [_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) Call: member([_2968,carolina_panthers,_2972,fialovy],[[_2896,green_bay_packers,zadni_utocnik,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny], [_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) !!! dynamic(member/2) Call: member([_2968,carolina_panthers,_2972,fialovy],[[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny],[_2923,_2925,_2927,cerny], [_2932,_2934,_2936,fialovy]]) !!! dynamic(member/2) Call: member([_2968,carolina_panthers,_2972,fialovy],[[_2914,_2916,_2918,cerveny],[_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) !!! dynamic(member/2) Call: member([_2968,carolina_panthers,_2972,fialovy],[[_2923,_2925,_2927,cerny],[_2932,_2934,_2936,fialovy]]) !!! dynamic(member/2) Call: member([_2968,carolina_panthers,_2972,fialovy],[[_2932,_2934,_2936,fialovy]]) !!! dynamic(member/2) Exit: member([_2932,carolina_panthers,_2936,fialovy],[[_2932,carolina_panthers,_2936,fialovy]]) %splněna podmínka, že [_2932,carolina_panthers,_2936,fialovy] je prvkem struktury, ukončení testování Exit: member([_2932,carolina_panthers,_2936,fialovy],[[_2923,_2925,_2927,cerny],[_2932,carolina_panthers,_2936,fialovy]]) Exit: member([_2932,carolina_panthers,_2936,fialovy],[[_2914,_2916,_2918,cerveny],[_2923,_2925,_2927,cerny],[_2932,carolina_panthers,_2936,fialovy]]) Exit: member([_2932,carolina_panthers,_2936,fialovy],[[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny],[_2923,_2925,_2927,cerny], [_2932,carolina_panthers,_2936,fialovy]]) Exit: member([_2932,carolina_panthers,_2936,fialovy],[[_2896,green_bay_packers,zadni_utocnik,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny], [_2923,_2925,_2927,cerny],[_2932,carolina_panthers,_2936,fialovy]]) Call: member([samuel,_2979,_2845,_2983],[[_2896,green_bay_packers,zadni_utocnik,zluty],[_2905,_2907,_2909,modry],[_2914,_2916,_2918,cerveny], [_2923,_2925,_2927,cerny],[_2932,carolina_panthers,_2936,fialovy]]) !!! dynamic(member/2)
Prolog nejprve dosadí do prvků struktury libovolné hodnoty. Poté postupně dosazuje do prvků struktury jednotlivé hodnoty a vyhodnocuje části podmínky, které musí být splněny. 8
Další část výpisu trasování. Call: member([_2995,_2997,obrance,zluty],[[_2896,green_bay_packers,zadni_utocnik,zluty],[_2905,_2907,_2909,modry], [_2914,dallas_cowboys,krajni_utocnik,cerveny],[samuel,_2925,_2845,cerny],[_2932,carolina_panthers,_2936,fialovy]]) !!! dynamic(member/2) %zjišťování, zda [_2995,_2997,obrance,zluty] může být prvkem struktury, zjišťování probíhá pomocí predikátu member, který využívá rekurze Call: member([_2995,_2997,obrance,zluty],[[_2905,_2907,_2909,modry],[_2914,dallas_cowboys,krajni_utocnik,cerveny],[samuel,_2925,_2845,cerny], [_2932,carolina_panthers,_2936,fialovy]]) !!! dynamic(member/2) Call: member([_2995,_2997,obrance,zluty],[[_2914,dallas_cowboys,krajni_utocnik,cerveny],[samuel,_2925,_2845,cerny],[_2932,carolina_panthers,_2936,fialovy]]) !!! dynamic(member/2) Call: member([_2995,_2997,obrance,zluty],[[samuel,_2925,_2845,cerny],[_2932,carolina_panthers,_2936,fialovy]]) !!! dynamic(member/2) Call: member([_2995,_2997,obrance,zluty],[[_2932,carolina_panthers,_2936,fialovy]]) !!! dynamic(member/2) Call: member([_2995,_2997,obrance,zluty],[]) %poslední volání predikátu member v rámci rekurze !!! dynamic(member/2) Fail: member([_2995,_2997,obrance,zluty],[]) %predikát selhal, prvek [_2995,_2997,obrance,zluty] není prvkem struktury, která bude na konci řešením úlohy Fail: member([_2995,_2997,obrance,zluty],[[_2932,carolina_panthers,_2936,fialovy]]) Fail: member([_2995,_2997,obrance,zluty],[[samuel,_2925,_2845,cerny],[_2932,carolina_panthers,_2936,fialovy]]) Fail: member([_2995,_2997,obrance,zluty],[[_2914,dallas_cowboys,krajni_utocnik,cerveny],[samuel,_2925,_2845,cerny],[_2932,carolina_panthers,_2936,fialovy]]) Fail: member([_2995,_2997,obrance,zluty],[[_2905,_2907,_2909,modry],[_2914,dallas_cowboys,krajni_utocnik,cerveny],[samuel,_2925,_2845,cerny], [_2932,carolina_panthers,_2936,fialovy]]) %konec testování zda [_2995,_2997,obrance,zluty] je prvkem sktury, testování zkončilo neúspěšně
7 Zdroje informací [1] Prolog (programovací jazyk) - Wikipedie, otevřená encyklopedie [online] [cit. 5.1.2007]
9