Za´padoˇceska´ univerzita v Plzni Fakulta aplikovany´ch vˇed Katedra informatiky a vy´poˇcetn´ı techniky
Bakal´ aˇ rsk´ a pr´ ace Vytvoˇ ren´ı v´ yukov´ eho programu pro v´ yuku programovac´ıho jazyka Prolog
Plzeˇ n 2014
Tom´aˇs Cigler
Prohl´ aˇ sen´ı Prohlaˇsuji, ˇze jsem bakal´aˇrskou pr´aci vypracoval samostatnˇe a v´ yhradnˇe s pouˇzit´ım citovan´ ych pramen˚ u. V Plzni dne 27. ˇcervna 2014 Tom´aˇs Cigler
Abstract Creation of learning program for Prolog learning This bachelor thesis deals with creation of simple learning program for novice programmers or even advanced ones, who want to explore the world of constraint logic programming. You will find here lots of practical examples, which should guide you to understand different way of programming. After reading this thesis, you should be able to write Prolog programs on your own.
Abstrakt Vytvoˇ ren´ı v´ yukov´ eho programu pro v´ yuku programovac´ıho jazyka Prolog Tato pr´ace je zamˇeˇrena na zaˇc´ınaj´ıc´ı i pokroˇcil´e program´atory, kteˇr´ı chtˇej´ı z´ıskat povˇedom´ı o logick´em pˇr´ıstupu k programov´an´ı. Najdeme zde spoustu praktick´ ych uk´azek, kter´e se jednoduch´ ym zp˚ usobem snaˇz´ı uk´azat ˇcten´aˇr˚ um moˇznosti pouˇzit´ı tohoto odliˇsn´eho pˇr´ıstupu. Program´ator by mˇel b´ yt po pˇreˇcten´ı pr´ace schopen s´am vytv´aˇret programy v jazyce Prolog a samostatnˇe ˇreˇsit obvykl´e probl´emy souvisej´ıc´ı s logick´ ym programov´an´ım.
Obsah ´ 1 Uvod 2 Sezn´ amen´ı s jazykem Prolog 2.1 Co je Prolog? . . . . . . . . . 2.2 Motivace . . . . . . . . . . . . 2.3 V´ yvojov´e prostˇred´ı . . . . . . 2.3.1 Pˇrehled interpret´ator˚ u 2.3.2 Pˇr´ıprava prostˇred´ı . . .
1
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
3 Zaˇ c´ın´ ame programovat 3.1 Z´akladn´ı syntax a terminologie . . . . . . . 3.2 Vlastn´ı program a uk´azkov´e pˇr´ıklady . . . . 3.2.1 Z´apis k´odu . . . . . . . . . . . . . . . ˇ y k´od . . . . . . . . . . . . . . . 3.2.2 Cist´ 3.2.3 Naˇcten´ı programu a z´akladn´ı dotazy 3.2.4 Promˇenn´e a pravidla v datab´azi . . .
. . . . .
. . . . . .
. . . . .
. . . . . .
. . . . .
. . . . . .
. . . . .
. . . . . .
. . . . .
. . . . . .
. . . . .
. . . . . .
. . . . .
. . . . . .
. . . . .
. . . . . .
. . . . .
. . . . . .
. . . . .
2 2 2 3 3 5
. . . . . .
6 6 8 8 9 10 11
ˇ 4 Cinnost interpret´ atoru a standardn´ı predik´ aty 4.1 Vyhodnocov´an´ı c´ıl˚ u. . . . . . . . . . . . . . . . 4.2 Jak funguje backtracking? . . . . . . . . . . . . 4.3 Unifikace . . . . . . . . . . . . . . . . . . . . . . ˇ ızen´ı backtrackingu . . . . . . . . . . . . . . . 4.4 R´ ˇ ızen´ı v pravidlech . . . . . . . . . . . . 4.4.1 R´ 4.5 N´avratov´a hodnota . . . . . . . . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
13 13 14 15 16 18 19
5 Standard ISO Prolog 5.1 Oper´atory . . . . . . . . . . . . . . 5.1.1 Definice vlastn´ıch oper´ator˚ u 5.2 Probl´em symetrick´ ych relac´ı . . . . 5.2.1 Podm´ınka . . . . . . . . . . 5.2.2 Abstrakce . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
21 21 22 23 24 25
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
5.3 5.4 5.5 5.6
Vstup od uˇzivatele . . . . . Cyklus . . . . . . . . . . . . Rekurze . . . . . . . . . . . Seznamy . . . . . . . . . . . 5.6.1 Pˇrid´av´an´ı prvk˚ u. . . 5.6.2 Zpracov´av´an´ı prvk˚ u. ˇ ezce . . . . . . . . . . . 5.7 Retˇ 5.8 Pr´ace se soubory . . . . . . ˇ ı. . . . . . . . . 5.8.1 Cten´ 5.8.2 Z´apis . . . . . . . . . 5.8.3 Jin´ y zp˚ usob . . . . . 5.9 Moduly . . . . . . . . . . . ´ 5.10 Uprava nahran´e datab´aze .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
26 27 28 29 29 30 31 33 33 36 36 38 39
6 V´ yvojov´ e prostˇ red´ı B-Prolog 6.1 Zmˇeny oproti standardu . . . . . . . . . . . . 6.1.1 Vlastn´ı programy . . . . . . . . . . . . 6.1.2 Komunikace s OS . . . . . . . . . . . . 6.1.3 Vol´an´ı predik´at˚ u . . . . . . . . . . . . 6.1.4 Pr´ace se seznamy . . . . . . . . . . . . 6.1.5 Cyklus, kolekce a form´atov´an´ı v´ ystupu 6.1.6 M´od ladˇen´ı . . . . . . . . . . . . . . . 6.1.7 Glob´aln´ı promˇenn´e . . . . . . . . . . . 6.2 Propojen´ı s imperativn´ımi jazyky . . . . . . . 6.3 Propojen´ı s jazykem C . . . . . . . . . . . . . 6.3.1 Vol´an´ı C z Prologu . . . . . . . . . . . 6.3.2 Vol´an´ı Prologu z C . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
41 41 41 42 42 43 44 45 46 47 47 47 49
7 Z´ avˇ er
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
50
´ 1 Uvod Pr´ace je c´ılena na skupinu zaˇc´ınaj´ıc´ıch i pokroˇcil´ ych program´ator˚ u, kteˇr´ı chtˇej´ı z´ıskat zkuˇsenosti s pˇr´ıstupem k programov´an´ı logick´ ych u ´loh. Jedn´a se o z´aklady logick´eho programov´an´ı v jazyce Prolog, s rozˇs´ıˇren´ım o nadstandardn´ı moˇznosti implementace B-Prolog. Prostudov´an´ım jednotliv´ ych kapitol a pˇr´ıklad˚ u s nimi u ´zce spjat´ ych se ˇcten´aˇr postupnˇe dozv´ı, jak s programov´an´ım zaˇc´ıt, co k tomu vˇse potˇrebuje a n´asleduje pˇredstaven´ı cel´eho procesu programov´an´ı. Pˇri ˇcten´ı by ˇcten´aˇr nemˇel m´ıt pocit informaˇcn´ıho pˇresycen´ı, pˇresto je zachov´ana informaˇcn´ı hodnota a jej´ı vzr˚ ustaj´ıc´ı trend. Prvn´ı kapitoly jsou urˇceny u ´pln´ ym zaˇc´ateˇcn´ık˚ um a informace jsou pod´any jednoduˇse a pˇrirozenˇe. Metoda v´ yuky umoˇzn ˇuje zvolit si k pr´aci i jin´ y interpret´ator, neˇz byl k psan´ı a spouˇstˇen´ı program˚ u v´ yhradnˇe pouˇz´ıv´an. Velk´a ˇc´ast pr´ace je realizov´ana za pomoci standardn´ıch z´apis˚ u Prologu s n´asledn´ ym obohacen´ım o dalˇs´ı funkce B-Prologu. Z´avˇerem se ˇcten´aˇr dozv´ı o pozitivech a negativech propojen´ı logick´eho a imperativn´ıho programov´an´ı v jeden funkˇcn´ı celek a moˇznosti pouˇzit´ı ve velk´ ych projektech. Posledn´ı kapitoly kladou vyˇsˇs´ı n´aroky na znalosti klasick´eho programov´an´ı v kombinaci se znalostmi z´ıskan´ ymi v pˇredchoz´ıch kapitol´ach t´eto pr´ace.
1
2 Sezn´amen´ı s jazykem Prolog 2.1
Co je Prolog?
Programovac´ı jazyk Prolog vznikl na zaˇc´atku 70. let ve Francii. P. Roussel jej zaˇcal vyv´ıjet za u ´ˇcelem umoˇznˇen´ı komunikace ˇclovˇeka s poˇc´ıtaˇcem v pˇrirozen´em jazyce. Prolog pracuje na b´azi predik´atov´e logiky prvn´ıho ˇr´adu. Jednoduˇse ˇreˇceno, je to odvozov´an´ı poznatk˚ u na z´akladˇe zn´am´ ych fakt˚ u a pravidel neboli vypl´ yv´an´ı. Modern´ı vysoko´ urovˇ nov´e jazyky (napˇr. Java) jsou imperativn´ı. Programy popisuj´ı algoritmus, kter´ ym se ˇreˇs´ı specifick´ y probl´em, a vyuˇz´ıv´a se k tomu vˇetˇsinou procedur´aln´ı programov´an´ı a objekty. Prolog je zkratka anglick´e vˇety Programming in logic“. Ta znaˇc´ı, ˇze se jedn´a o logick´e programov´an´ı. ” Definujeme pouze co se m´a vyhodnotit a neˇreˇs´ıme zp˚ usob, ˇc´ımˇz se Prolog ˇrad´ı do deklarativn´ıch programovac´ıch jazyk˚ u. Vnitˇrn´ı algoritmy vyhodnocov´an´ı dotaz˚ u (c´ıl˚ u) jsou pro n´as skryt´e. T´ım je jazyk pˇr´ıvˇetivˇejˇs´ı i pro laiky, nebot’ nen´ı tˇreba zn´at architekturu a princip fungov´an´ı poˇc´ıtaˇce [8]. Pro snadnou orientaci v k´odu je d˚ uleˇzit´e zn´at z´aklady predik´atov´e logiky. Program´ator vytv´aˇr´ı posloupnost urˇcit´ ych v´ yrok˚ u (predik´at˚ u), kter´e dohromady v dan´em sledu a kontextu vytv´aˇrej´ı datab´azi pro reprezentaci znalost´ı. Jazyk je interpretovan´ y, ˇcili nem´ame pˇredem urˇcen´ y postup ˇreˇsen´ı, ale zad´av´ame dotazy, kter´e chceme vyhodnotit. K tomu interpret´ator vyuˇz´ıv´a rekurzi, unifikaci a backtracking (podrobnˇeji viz kapitola 4).
2.2
Motivace
Uˇz na u ´vod m˚ uˇzeme nam´ıtat, ˇze jazyk Prolog dnes nen´ı ve svˇetˇe rozˇs´ıˇren´ y a bude m´ıt mal´e vyuˇzit´ı. Rozhodnˇe jej nevyuˇzijeme pro sloˇzit´e matematick´e v´ ypoˇcty, vykreslov´an´ı grafiky a podobn´e v´ ypoˇcetnˇe n´aroˇcn´e akce, kde potˇrebujeme vytv´aˇret v´ ykonn´e algoritmy. Prolog m´a ale vlastnosti, d´ıky kter´ ym se v´ ybornˇe hod´ı na specifick´e u ´kony. Napˇr´ıklad jiˇz zmiˇ novan´a komunikace se strojem v pˇrirozen´em jazyce byla vyuˇzita v roce 2005 agenturou NASA ve vesm´ırn´e stanici. V syst´emech umˇel´e inteligence naˇsel Prolog vyuˇzit´ı v ned´avn´e dobˇe i pˇri programov´an´ı superpoˇc´ıtaˇce Watson od IBM. Stejnˇe d˚ uleˇzit´e jsou znalostn´ı b´aze, chytr´e“ vyhle” d´av´an´ı apod. [1] 2
Sezn´amen´ı s jazykem Prolog
V´yvojov´e prostˇred´ı
Prolog pˇrin´aˇs´ı jin´ y pohled na programov´an´ı a pˇrechod z klasick´eho pˇr´ıstupu m˚ uˇze b´ yt pro nˇekter´e program´atory v´ yzva. V Prologu se daj´ı napsat rychle jednoduch´e a efektivn´ı aplikace, kter´e jsou k´odovˇe transparentn´ı a tedy snadno udrˇziteln´e, coˇz je vˇetˇsinou naˇs´ım c´ılem. V kombinaci s imperativn´ım jazykem, jako je Java nebo C, m´a Prolog jeˇstˇe mnohem vˇetˇs´ı potenci´al. Nejprve si ˇrekneme, jak s programov´an´ım v Prologu vlastnˇe zaˇc´ıt.
2.3
V´ yvojov´ e prostˇ red´ı
U kaˇzd´eho vysoko´ urovˇ nov´eho programovac´ıho jazyka potˇrebujeme software, kter´ y n´am umoˇzn´ı spuˇstˇen´ı programu, aniˇz bychom museli zn´at niˇzˇs´ı programovac´ı jazyky. U kompilovan´ ych jazyk˚ u p´ıˇseme k´od v n´ami zvolen´em textov´em editoru a n´aslednˇe pouˇzijeme pˇrekladaˇc, kter´ y pˇreloˇz´ı n´aˇs program do mezik´odu nebo strojov´eho k´odu. Jazyk Prolog je ale interpretovan´ y, proto potˇrebujeme vhodn´ y interpret´ator. Prvn´ı intepret´ator naprogramoval jiˇz zm´ıˇ nˇen´ y P. Roussel spoleˇcnˇe s A. Colmerauerem v roce 1972. Postupnˇe se rozˇsiˇrovaly implementace a kaˇzd´a v sobˇe obsahovala trochu jinou sadu zabudovan´ ych predik´at˚ u. Kv˚ uli pˇrenositelnosti Prologovsk´ ych program˚ u bylo nutn´e zav´est pevn´ y standard. N´avrh standardu sepsal v roce 1984 R. O’Keefe, pˇresto ho m´alokter´a verze Prologu dodrˇzovala. V pr˚ ubˇehu let se standard upˇresˇ noval a v roce 1993 vyˇsla dalˇs´ı neofici´aln´ı sumarizace n´avrhu. V roce 1995 byl standard ofici´alnˇe zaveden, ale aˇz v posledn´ıch nˇekolika letech je implementacemi v´ıce dodrˇzov´an [6]. Rozhran´ı kaˇzd´eho interpret´atoru je odliˇsn´e, podporuje jin´e z´apisy a stejn´e predik´aty mohou v r˚ uzn´ ych verz´ıch fungovat trochu jinak. N´as budou zaj´ımat pouze implementace podporuj´ıc´ı ISO Prolog. Z´aleˇz´ı tak´e na platformˇe, na kter´e budeme pracovat, tzn. zda bude implementace fungovat na syst´emu Windows, Linux nebo Mac OS. V praxi budeme cht´ıt Prolog vyuˇz´ıt k efektivn´ımu ˇreˇsen´ı komplexnˇejˇs´ıch probl´em˚ u. R˚ uzn´e implementace se hod´ı na odliˇsn´e typy u ´loh.
2.3.1
Pˇ rehled interpret´ ator˚ u
K dispozici je cel´a ˇrada starˇs´ıch implementac´ı, kter´e se neust´ale vyv´ıjej´ı. Vˇetˇsina z nich je vytvoˇrena na otevˇren´ ych licenc´ıch. Proto nen´ı nutn´e sahat pro zpoplatnˇen´e verze, jako je napˇr. SICStus Prolog, kter´ y je velice drah´ y. Na internetov´ ych diskuz´ıch a v ˇcl´anc´ıch najdeme uˇzivatelsk´a doporuˇcen´ı a hodnocen´ı r˚ uzn´ ych implementac´ı. Pˇrehledn´e srovn´an´ı sepsal C. Heng na sv´ ych 3
Sezn´amen´ı s jazykem Prolog
V´yvojov´e prostˇred´ı
internetov´ ych str´ank´ach [5]. Bohuˇzel pˇrehled jiˇz nen´ı aktu´aln´ı, proto se pod´ıv´ame na kaˇzdou implementaci zvl´aˇst’. Nejzaj´ımavˇejˇs´ı jsou pro n´as interpret´atory s moˇznost´ı propojen´ı s jin´ ymi jazyky. To znamen´a, ˇze interpret´ator dok´aˇze pˇreloˇzit a prov´est k´odovou sekvenci tˇechto jazyk˚ u. Ekvivalentnˇe m˚ uˇzeme v procedur´aln´ım programu spolupracovat s interpret´atorem, kter´ y zde m˚ uˇze vyˇreˇsit d´ılˇc´ı probl´em efektivnˇeji, neˇz p˚ uvodn´ı programovac´ı jazyk. • tuProlog – intepret´ator je vytvoˇren pod licenc´ı GNU LGPL. Skl´ad´a se z minimalistick´eho j´adra, kter´e zvl´ad´a z´akladn´ı pr´aci s Prologem a lze d´ale rozˇs´ıˇrit dalˇs´ımi sadami predik´at˚ u. P˚ uvodnˇe byl vytvoˇren pro webov´e applety. V´ ysledn´e programy kompiluje do .JAR form´atu, takˇze jsou spustiteln´e napˇr´ıˇc platformami s JVM1 . D´ale existuje verze pro Android a .NET. S tˇemito jazyky je potom kompatibiln´ı. • Visual Prolog – podporuje pouze platformu Windows. Kombinuje logick´e, klasick´e i objektov´e programov´an´ı. Je kompatibiln´ı s jazyky C a C++ a m˚ uˇze pouˇz´ıvat funkce Win32. Prostˇred´ı podporuje i grafick´e vytv´aˇren´ı a u ´pravy formul´aˇr˚ u a oken. Pro soukrom´e u ´ˇcely je Visual Prolog zdarma, komerˇcnˇe se m˚ uˇze vyuˇz´ıvat aˇz se zaplacenou licenc´ı. • Ciao Prolog – v z´akladu plnˇe podporuje ISO standard, ale mohou se pˇridat pro kaˇzd´ y projekt rozˇsiˇruj´ıc´ı moduly. Posledn´ı verze je ze srpna roku 2011 a funguje na vˇsech nejpouˇz´ıvanˇejˇs´ıch platform´ach. Ciao Prolog se m˚ uˇze propojit s jazyky C i Java anebo s relaˇcn´ımi datab´azemi. Existuj´ı i moduly pro s´ıt’ovou komunikaci a webov´e aplikace. • C#Prolog – st´ale aktualizovan´a verze pod licenc´ı GNU GPLv2. Podporuje jednoduch´e propojen´ı s jazykem C# a tedy i datab´azemi, se kter´ ymi tento jazyk dok´aˇze pracovat. M´a podporu pro form´at XML a st´ale se rozˇsiˇruj´ıc´ı struktury JSON. Funguje na platformˇe Windows i Linux. • GNU Prolog – kromˇe standardu ISO obsahuje v´ıce neˇz 300 dalˇs´ıch zabudovan´ ych predik´at˚ u. Podporuje ladˇen´ı a propojen´ı s jazykem C. Obsahuje optimalizovan´ y a rychl´ y kompil´ator, kter´ y vytv´aˇr´ı jednoduch´e spouˇstˇec´ı soubory. Pracuje na vˇsech z´akladn´ıch platform´ach a je vytvoˇren pod licenc´ı GNU LGPL. 1
Java Virtual Machine – virtu´ aln´ı prostˇred´ı, kter´e umoˇzn ˇuje spustit programy kompilovan´e do bin´ arn´ıho mezik´ odu jazyka Java
4
Sezn´amen´ı s jazykem Prolog
V´yvojov´e prostˇred´ı
• SWI Prolog – nejvˇetˇs´ı projekt na voln´e GNU GPL licenci, kter´ y se vyv´ıj´ı jiˇz od roku 1987. Standard ISO Prolog je rozˇs´ıˇren o robustn´ı grafick´e rozhran´ı s moˇznost´ı ladˇen´ı program˚ u a pˇrid´an´ı rozˇsiˇruj´ıc´ıch bal´ıˇck˚ u. M˚ uˇzeme jej propojit s jazykem C a C++, datab´azemi, pˇr´ıpadnˇe nahr´at hotovou z´akladn´ı knihovnu pro rozpozn´av´an´ı pˇrirozen´eho jazyka apod. Samozˇrejmost´ı je podpora vˇsech nejpouˇz´ıvanˇejˇs´ıch platforem. • B-Prolog – implementace od spol. Afany Software, kter´a tak´e splˇ nuje standard ISO Prolog, dok´aˇze obousmˇernˇe spolupracovat s jazyky C, C++ a Java a pro osobn´ı, nekomerˇcn´ı nebo akademick´e u ´ˇcely je zdarma. Podrobnˇeji si ji pˇredstav´ıme v kapitole 6.
2.3.2
Pˇ r´ıprava prostˇ red´ı
Kdyˇz uˇz m´ame povˇedom´ı o tom, co to Prolog vlastnˇe je a k ˇcemu jej m˚ uˇzeme vyuˇz´ıt, vybereme si vhodnou implementaci interpret´atoru. Pro dalˇs´ı pr´aci a uk´azky budu po celou dobu vyuˇz´ıvat pr´avˇe B-Prolog na syst´emu Linux. Instalace interpret´atoru je jednoduch´a. Staˇc´ı z ofici´aln´ıch webov´ ych str´anek http://www.probp.com/download.html st´ahnout nejnovˇejˇs´ı verzi pro naˇsi platformu, rozbalit do poˇzadovan´eho um´ıstˇen´ı pomoc´ı archivaˇcn´ıho programu a spustit bp.exe na platformˇe Windows, eventu´alnˇe na Linuxu soubor bp v pˇr´ıkazov´e ˇra´dce. V obou pˇr´ıpadech pˇred sebou vid´ıme konzolovou aplikaci, kter´a vyp´ıˇse na obrazovku (v m´em pˇr´ıpadˇe): B-Prolog Version 8.1, All rights reserved, (C) Afany Software 1994-2014. | ?-
Pokud se V´am B-Prolog nezamlouv´a, pro z´aklady jazyka nen´ı d˚ uleˇzit´e, jakou implementaci si vyberete. N´asleduj´ıc´ı postupy by mˇely ve vˇsech implementac´ıch podporuj´ıc´ıch standard ISO Prolog fungovat totoˇznˇe.
5
3 Zaˇc´ın´ame programovat Jelikoˇz B-Prolog plnˇe podporuje ˇcesk´e znaky v sadˇe UTF-8, mohli bychom pouˇz´ıvat ˇcesk´a slova vˇcetnˇe diakritiky. Z d˚ uvodu pˇrenositelnosti by ale bylo nejlepˇs´ı ps´at k´od v angliˇctinˇe. Knihy zab´ yvaj´ıc´ı se v´ yukou jazyka Prolog se vyskytuj´ı pˇrev´aˇznˇe v anglick´em jazyce, proto zde budu pro zmˇenu v uk´azkov´ ych pˇr´ıkladech pouˇz´ıvat ˇceˇstinu, ale bez diakritiky. Prostˇred´ı n´as nyn´ı vyz´ yv´a k z´apisu pˇr´ıkaz˚ u (anglicky prompt), je to vlastnˇe interaktivn´ı m´od interpret´atoru. Vˇetˇsina knih, n´avod˚ u na internetu i odborn´ ych ˇcl´ank˚ u t´ ykaj´ıc´ıch se v´ yuky programovac´ıho jazyka zaˇc´ın´a stejnˇe – vypsat na standardn´ı v´ ystup (tedy vˇetˇsinou na obrazovku) text Hello world!“ ” neboli v pˇrekladu Ahoj, svˇete!“ Pod´ıv´ame se, jak se to dˇel´a v Prologu: ” | ?- write(’Ahoj, svete!’). Ahoj, svete! yes
ˇ ıkali jsme si, ˇze Prolog vyhodnocuje dotazy v z´avislosti na predik´atech R´ v datab´azi, kterou jsme ale zat´ım sami nevytvoˇrili. Predik´at write je souˇc´ast´ı standardu ISO Prolog, kter´ y je v interpret´atoru zabudov´an. V´ ypis textu Ahoj, svete!“ na obrazovku je u vyhodnocen´ı tohoto predik´atu vlastnˇe ved” lejˇs´ı efekt. Slovo yes na konci znamen´a, ˇze byl c´ıl vyhodnocen u ´spˇeˇsnˇe. Pokud by se vyhodnocen´ı nepovedlo, interpret´ator vyp´ıˇse no. At’ uˇz interpret´ator vyhodnot´ı predik´at u ´spˇeˇsnˇe ˇci ne´ uspˇeˇsnˇe, n´asleduje opˇet interaktivn´ı m´od, takˇze je na obrazovku vyps´an prompt a ˇcek´a se na vstup od uˇzivatele. V´ yjimkou je zabudovan´ y predik´at halt, kter´ y ukonˇc´ı pr´aci interpret´atoru. O dalˇs´ıch zabudovan´ ych predik´atech si pov´ıme v kapitole 5.
3.1
Z´ akladn´ı syntax a terminologie
ˇ adek write(’Ahoj, svete!’). je z logiky term. Kaˇzd´ R´ y term je v Prologu ukonˇcen teˇckou. v tomto pˇr´ıpadˇe zastupuje dotaz (c´ıl), kter´ y chceme vyhodnotit. Termy se v Prologu dˇel´ı na konstanty (ˇc´ısla nebo atomy), promˇenn´e, struktury a pravidla. Struktura – ˇc´ast pˇred z´avorkou write se naz´ yv´a hlava, pˇresnˇeji funktor, kter´ y mus´ı b´ yt obecnˇe atom. Uvnitˇr z´avorek je tak´e atom ’Ahoj, svete!’, ale obecnˇe to m˚ uˇze b´ yt jak´ ykoliv jin´ y term. 6
Zaˇc´ın´ame programovat
Z´akladn´ı syntax a terminologie
Term˚ um uvnitˇr z´avorek se ˇr´ık´a argumenty. Aˇz budeme pozdˇeji definovat vlastn´ı fakta, argument˚ u m˚ uˇze b´ yt libovoln´ y poˇcet a jsou oddˇelen´e ˇc´arkou. Kdyˇz nepouˇzijeme ˇz´adn´ y argument, vynech´ame i z´avorky. Poˇcet argument˚ u se oznaˇcuje jako arita. Pokud mluv´ıme o konkr´etn´ı struktuˇre, popisujeme ji zp˚ usobem – funktor/arita, konkr´etnˇe write/1. Jestliˇze maj´ı struktury totoˇzn´ y funktor, ale odliˇsnou aritu, jsou tak´e odliˇsn´e. Podle arity naz´ yv´ame struktury nul´arn´ı (bez argument˚ u, takt´eˇz naz´ yv´ame objekty), un´arn´ı (1 argument), bin´arn´ı (2 argumenty) apod. Atom je jin´ ymi slovy urˇcit´ y bezkontextov´ y n´apis“, neboli objekt. Zapi” sujeme jej tˇremi zp˚ usoby. 1) Mus´ı zaˇc´ınat mal´ ym p´ısmenem a jako dalˇs´ı znaky jsou kromˇe mal´ ych a velk´ ych p´ısmen povoleny pouze ˇc´ıslice a podtrˇz´ıtka. 2) Libovoln´ y ˇretˇezec uzavˇren´ y v jednoduch´ ych uvozovk´ach, kde m˚ uˇzeme pouˇz´ıvat i speci´aln´ı znaky (napˇr. ’Ahoj, svete!’). 3) Jeden (nebo posloupnost) z povolen´ ych speci´aln´ıch znak˚ u (+ - * / apod.). D´ale si nˇekter´e z nich uk´aˇzeme. ˇ ıslo m˚ C´ uˇze b´ yt cel´e (integer) nebo s plovouc´ı desetinnou ˇca´rkou a nen´ı omezen´e velikost´ı. Integer m˚ uˇzeme zapisovat v des´ıtkov´e (567), osmiˇckov´e (0o123), hexadecim´aln´ı (0xC2A) i bin´arn´ı (0b011) soustavˇe. Desetinn´e ˇc´ıslo zapisujeme pouze v des´ıtkov´e soustavˇe a pouˇz´ıv´ame desetinnou teˇcku. Z´apisy mohou vypadat n´asledovnˇe: 5.33 -5.33 5.33E2 5.33E-5 kde E je exponent. Z´apis 5. nebo .33 nen´ı povolen. Promˇ enn´ a se vˇzdy zapisuje s poˇc´ateˇcn´ım velk´ ym p´ısmenem nebo podtrˇz´ıtkem a mohou n´asledovat dalˇs´ı znaky. M˚ uˇzeme pouˇz´ıt i ˇc´ıslice. Pro jednoduch´e programy pouˇz´ıv´ame bˇeˇznˇe jednop´ısmenn´e n´azvy, u sloˇzitˇejˇs´ıch bychom mˇeli pojmenov´avat promˇenn´e dle kontextu, abychom se v programu vyznali. Pokud je promˇenn´a zaps´ana pouze podtrˇz´ıtkem, ˇr´ık´ame, ˇze je anonymn´ı. Interpret´ator ji pˇriˇrad´ı hodnotu, ale po vyhodnocen´ı ji nezmiˇ nuje, ani s n´ı nem˚ uˇzeme d´ale pracovat. Promˇenn´e se dosazuj´ı do struktur, kter´e chceme urˇcit abstraktnˇe. Pouˇz´ıvaj´ı se m´ısto konkr´etn´ıch objekt˚ u. Seznam se chov´a obdobnˇe jako v jazyce LISP – kaˇzd´ y seznam m´a hlavu (prvn´ı prvek) a tˇelo (orig. tail“), coˇz je zbytek seznamu. V´ yˇcet prvk˚ u se” znamu se zapisuje do hranat´ ych z´avorek – [a, b, c]. Hod´ı se pˇrev´aˇznˇe pro pˇr´ıpady, kdy potˇrebujeme pro nˇejak´ y funktor promˇenliv´ y poˇcet argument˚ u. Nepotˇrebujeme pˇredem vˇedˇet, kolik jich bude a se-
7
Zaˇc´ın´ame programovat
Vlastn´ı program a uk´azkov´e pˇr´ıklady
znam se vˇzdy pˇrizp˚ usob´ı. M˚ uˇze obsahovat atomy, struktury nebo dalˇs´ı seznamy. Seznamy se mohou vnoˇrovat neomezenˇe: [prvek1, predikat(1, X), Y, [1,2,3]]
3.2
Vlastn´ı program a uk´ azkov´ e pˇ r´ıklady
Kdyˇz uˇz zn´ame z´akladn´ı syntax a stavebn´ı prvky Prologu, vytvoˇr´ıme si vlastn´ı program, kter´ ym definujeme relaˇcn´ı datab´azi. K tomu potˇrebujeme libovoln´ y textov´ y editor. Vytvoˇr´ıme si pr´azdn´ y textov´ y soubor, kter´ y si vhodnˇe pojmenujeme. Jako pˇr´ıpona souboru se dle konvenc´ı pouˇz´ıv´a .pl. Ze zkuˇsenost´ı uˇzivatel˚ u interpret´atoru SWI Prolog se ale v nˇekter´ ych pˇr´ıpadech m˚ uˇze pˇriˇrazen´ı t´eto koncovky kr´ yt se zdrojov´ ymi soubory program˚ u jazyka Perl. V tˇechto pˇr´ıpadech tv˚ urci SWI Prologu v dokumentaci doporuˇcuj´ı pouˇz´ıvat pˇr´ıponu .pro [10].
3.2.1
Z´ apis k´ odu
Pro uk´azku si navrhneme malou datab´azi dom´ac´ıch mazl´ıˇck˚ u do souboru Mazlicci.pro: % Psi pes(alik). pes(rony). pes(besi). % Kocky kocka(micka). kocka(mnauka). kocka(andy). % Kralici kralik(ferda). kralik(matilda).
Programov´ y segment 3.1: Mazl´ıˇcci Pod´ıvejme se bl´ıˇze na v´ yznam tohoto k´odu – definovali jsme si celkem 8 fakt˚ u pomoc´ı tˇrech un´arn´ıch predik´at˚ u pes, kocka a kralik. Predik´aty n´am urˇcuj´ı relace mezi termy. Jako je zaps´an fakt v Prologu pes(rony), pˇrirozenˇe m˚ uˇzeme ˇr´ıct Rony je pes“. ” U n´ami vytvoˇren´ ych program˚ u velice z´aleˇz´ı na poˇrad´ı predik´at˚ u. Prolog pˇri vyhodnocov´an´ı postupuje v datab´azi shora dol˚ u. V programu 3.1 rozd´ıl nepozn´ame, ale kdyˇz zaˇcneme definovat sloˇzitˇejˇs´ı pravidla, na probl´em s poˇrad´ım z´apisu m˚ uˇzeme narazit.
8
Zaˇc´ın´ame programovat
Vlastn´ı program a uk´azkov´e pˇr´ıklady
Na prvn´ım ˇra´dku znakem % zaˇc´ın´a jednoˇr´adkov´ y koment´aˇr a aˇz do odˇra´dkov´an´ı m˚ uˇzeme ps´at jak´ekoliv znaky, kter´e nebudou interpret´atorem zpracov´any. Pokud chceme ps´at koment´aˇr na v´ıce ˇr´adk˚ u, ohraniˇc´ıme text posloupnost´ı znak˚ u /* na zaˇca´tku koment´aˇre a */ na konci. Za kaˇzd´ ym termem mus´ı b´ yt naps´ana teˇcka. Ta slouˇz´ı k odliˇsen´ı zaˇc´atku z´apisu dalˇs´ıho termu stejnˇe jako stˇredn´ıky v jazyce Java. Proto m˚ uˇzeme v programech zapsat v´ıce term˚ u na jednu ˇra´dku. Pokud budeme cht´ıt, tak m˚ uˇzeme na jednu ˇra´dku zapsat cel´ y program. U rozs´ahl´ ych webov´ ych projekt˚ u se d´ıky t´eto vlastnosti mohou vyuˇz´ıt tzv. minimiz´ery, kter´e automaticky odstran´ı vˇsechna odˇra´dkov´an´ı a koment´aˇre, takˇze se v´ ysledn´ y soubor o nˇeco zmenˇs´ı a rapidnˇe se zhorˇs´ı ˇcitelnost pro pˇr´ıpadn´e plagi´atory.
3.2.2
ˇ y k´ Cist´ od
Koment´aˇre jsou u rozs´ahlejˇs´ıch program˚ u nutnost´ı. Nˇekteˇr´ı program´atoˇri v praxi nam´ıtaj´ı, ˇze nejlepˇs´ım koment´aˇrem m´a b´ yt k´od samotn´ y. Vzhledem k transparentnosti Prologovsk´ ych z´apis˚ u bychom se toho mohli drˇzet, ale jestliˇze p´ıˇseme sloˇzitˇejˇs´ı pravidla anebo nejasn´e ˇci na prvn´ı pohled nelogick´e z´apisy, bylo by dobr´e koment´aˇre pouˇz´ıvat. K pˇrehlednosti patˇr´ı i spr´avn´e a logick´e pojmenov´an´ı predik´at˚ u. Program´ator si jistˇe m˚ uˇze uˇsetˇrit pr´aci t´ım, ˇze bude definovat predik´aty se zkratkovit´ ymi n´azvy. Koment´aˇre tak´e stoj´ı nˇejak´ y ˇcas. Pod´ıvejme se, jak m˚ uˇze vypadat nedbale zapsan´a stejn´a datab´aze mazl´ıˇck˚ u. Pˇredstav´ıme si n´aˇs myˇslenkov´ y pochod: p(alik). k(micka). k(mnauka). p(rony). p(besi). k(andy). Takov´ y z´apis se m˚ uˇze objevit pr´avˇe v pˇr´ıpadech, kdy chceme Prologovsk´ y k´od spojit s jin´ ym jazykem, protoˇze vnitˇrn´ı z´apisy jsou pro koncov´eho uˇzivatele neviditeln´e. Program´ator definuje postupnˇe jednotliv´a zv´ıˇrata, jak mu pˇrijdou pod ˇ ˇ adn´a jin´a zat´ım neporuku. Reknˇ eme, ˇze p/1 pˇredstavuje psa a k/1 koˇcku. Z´ tˇrebuje a bude s nimi d´ale pracovat v Javˇe. N´aslednˇe bude cht´ıt do datab´aze pˇrid´avat jeˇstˇe kr´al´ıky. Predik´at k/1 ale jiˇz zab´ıraj´ı koˇcky. Pouˇzije kr/1 a pˇrid´a dalˇs´ı fakta. Takto m˚ uˇze vypadat v´ ysledn´ y k´od: p(alik). k(micka). k(mnauka). p(rony). p(besi). k(andy). kr(ferda). kr(matilda).
Porovnejme nyn´ı z´apis s programem 3.1. Ve v´ ysledku jistˇe budou oba programy fungovat. Pozn´ate ale program´ator˚ uv z´amˇer? Nejsp´ıˇs ano, ale bude to 9
Zaˇc´ın´ame programovat
Vlastn´ı program a uk´azkov´e pˇr´ıklady
trvat d´ele neˇz u ˇcistˇe napsan´eho k´odu. Jistˇe si tv˚ urce takov´eho programu uˇsetˇril p´ar minut ˇcasu, kter´ y by mu zabralo form´atov´an´ı, delˇs´ı n´azvy nebo koment´aˇre, ale s´am bude za mˇes´ıc chv´ıli pˇrem´ yˇslet, co vlastnˇe znamen´a predik´at kr/1, jestli to tˇreba nen´ı kˇreˇcek. ˇ adn´ Z´ y ˇcas nedbal´ ym pˇr´ıstupem neuˇsetˇr´ıme. Kdyby mˇeli v budoucnu na naˇsem projektu pracovat i extern´ı program´atoˇri, takov´ y k´od je nepˇrijateln´ y. V t´eto pr´aci se budu drˇzet obdobn´eho z´apisu jako v p˚ uvodn´ı uk´azce.
3.2.3
Naˇ cten´ı programu a z´ akladn´ı dotazy
Pod´ıvejme se, jak s naˇs´ım programem m˚ uˇzeme pracovat. Kdyˇz spust´ıme interpret´ator klasickou cestou, jeho vnitˇrn´ı datab´aze obsahuje pouze z´akladn´ı zabudovan´e predik´aty. O naˇsem k´odu se mus´ı nˇejak´ ym zp˚ usobem dozvˇedˇet“. ” K tomu slouˇz´ı predik´at consult/1, jehoˇz argumentem je absolutn´ı ˇci relativn´ı cesta k naˇsemu souboru. Implementace B-Prolog 8.1 na Linuxu hled´a soubory relativnˇe od um´ıstˇen´ı, ze kter´eho byla spuˇstˇena, tedy z aktu´aln´ı otevˇren´e cesty v Shellu. Soubor Mazlicci.pro je um´ıstˇen ve sloˇzce, ze kter´e interpret´ator spouˇst´ıme. Staˇc´ı tedy pouˇz´ıt n´asleduj´ıc´ı z´apis: |?- consult(’Mazlicci.pro’). consulting::Mazlicci.pro yes
Interpret´ator potvrdil syntaktickou spr´avnost a pˇrid´an´ı naˇsich term˚ u do datab´aze. Abychom vidˇeli, co vˇsechno interpret´ator dosud nahr´al do sv´e vnitˇrn´ı datab´aze, pouˇzijeme predik´at listing/0: | ?- listing. kralik(ferda). kralik(matilda). pes(alik). pes(rony). pes(besi). kocka(micka). kocka(mnauka). kocka(andy). yes
Jak je vidˇet, interpret´ator seˇradil fakta tak, aby bylo vyhled´av´an´ı optimalizovan´e. Pokud jednotliv´e termy v souboru Mazlicci.pro zpˇreh´az´ıme, 10
Zaˇc´ın´ame programovat
Vlastn´ı program a uk´azkov´e pˇr´ıklady
intepret´ator vyp´ıˇse na obrazovku informaci ** Warning: Predicate is not defined contiguously: pes/1 a obdobnˇe i pro ostatn´ı predik´aty. Pˇresto jsou ve v´ ysledku vnitˇrnˇe seˇrazeny stejnˇe, jako u pˇredchoz´ıho programu. Pˇrehazuj´ı se mezi sebou ale pouze cel´e bloky spolu souvisej´ıc´ıch predik´at˚ u. Stejn´a fakta nebo pravidla mezi sebou kv˚ uli z´avislosti na poˇrad´ı interpret´ator prohodit samozˇrejmˇe nesm´ı. Na takov´e datab´azi si vyzkouˇs´ıme zad´av´an´ı c´ıl˚ u: | ?- kralik(ferda). yes | ?- pes(rony). yes | ?- pes(micka). no | ?- krecek(tony). *** Undefined procedure: krecek/1
Jestliˇze se pt´ame, zda Ferda je kr´al´ık a Rony je pes, intepret´ator odpov´ı kladnˇe, jelikoˇz je to pˇr´ımo jeden ze zn´am´ ych fakt˚ u. Micka je ale koˇcka, proto odpovˇed’ na dotaz, zda je Micka pes, je negativn´ı. Stejnˇe tak by byla negativn´ı, kdyˇz jako argument jednoho z predik´at˚ u pouˇzijeme v dotazu atom, kter´ y nen´ı mezi fakty uveden. Pˇri dotazu na kˇreˇcka interpret´ator vyp´ıˇse v´ yjimku, protoˇze jsme v programu predik´at krecek/1 nedefinovali.
3.2.4
Promˇ enn´ e a pravidla v datab´ azi
Na posledn´ım pˇr´ıkladu t´eto ˇca´sti si vysvˇetl´ıme z´apis promˇenn´ ych a pravidel. Pro tyto u ´ˇcely pouze rozˇs´ıˇr´ıme naˇsi datab´azi mazl´ıˇck˚ u. Definujeme nov´ y predik´at zvire/1. M˚ uˇzeme tvrdit, ˇze vˇsechna jm´ena v naˇs´ı datab´azi pˇredstavuj´ı jm´ena zv´ıˇrat. Abychom nemuseli definovat pro kaˇzd´ y atom nov´ y fakt (napˇr´ıklad ˇze Al´ık je zv´ıˇre, Micka je zv´ıˇre atp.), pouˇzijeme promˇennou a zap´ıˇseme n´asleduj´ıc´ı pravidlo: zvire(X) :- pes(X); kocka(X); kralik(X). Posloupnost znak˚ u :- m´a v´ yznam jako slovo kdyˇz“. Stˇredn´ık v tomto ” ˇ ˇ X je pes z´apisu znamen´a disjunkci neboli nebo“. Cteme X je zv´ıˇre, KDYZ ” ” NEBO koˇcka NEBO kr´al´ık“. Kromˇe disjunkce m˚ uˇzeme pouˇz´ıt jeˇstˇe logickou konjunkci a z´aroveˇ n“. K tomu slouˇz´ı oper´ator ,/2. Definujeme si pro uk´azku ” nˇekolik nov´ ych pravidel. Pˇrid´ame do datab´aze jeˇstˇe kˇreˇcka a ˇrekneme, ˇze psi, koˇcky a kˇreˇcci ˇzerou maso a kr´al´ıc´ı i kˇreˇcci ˇzerou b´ yl´ı. T´ım dok´aˇzeme odliˇsit skupiny masoˇzravc˚ u, b´ yloˇzravc˚ u a vˇseˇzravc˚ u: 11
Zaˇc´ın´ame programovat
Vlastn´ı program a uk´azkov´e pˇr´ıklady
% Krecci krecek(tony). % Strava zere_maso(X) :- pes(X); kocka(X); krecek(X). zere_byli(X) :- kralik(X); krecek(X). % Potravinove strategie masozravec(X) :- zere_maso(X), not zere_byli(X). bylozravec(X) :- zere_byli(X), not zere_maso(X). vsezravec(X) :- zere_byli(X), zere_maso(X).
V pravidlech se objevuje nov´ y predik´at not/1, kter´ y m´a v´ yznam negace predik´atu. Pokud tedy interpret´ator vyhodnot´ı u masoˇzravce predik´at y, not vr´at´ı opak a t´ım je nepravdiv´e cel´e pravidlo. zere byli/1 jako pravdiv´ O vyhodnocov´an´ı si bl´ıˇze pov´ıme v n´asleduj´ıc´ı kapitole. Znovu naˇcteme zmˇenˇen´ y program a zkus´ıme si nˇekolika dotazy funkˇcnost nov´eho ˇreˇsen´ı: | ?yes | ?no | ?no | ?yes
bylozravec(ferda). bylozravec(tony). masozravec(tony). vsezravec(tony).
Programov´ y segment 3.2: Potravinov´e strategie Abychom vyuˇzili cel´eho potenci´alu jazyka Prolog, je tˇreba pochopit z´akladn´ı principy, jak interpret´ator pracuje.
12
ˇ 4 Cinnost interpret´ atoru a standardn´ı predik´ aty Zkusme nyn´ı v aktu´alnˇe nahran´em programu Mazlicci.pro vyhodnotit c´ıl zvire(tony). Ze z´apisu v´ıme, ˇze tony je kˇreˇcek a v re´aln´em svˇetˇe je to jistˇe zv´ıˇre. Prolog n´am ale odpov´ı negativnˇe. Prologovsk´a datab´aze je totiˇz uzavˇren´ y svˇet, kde plat´ı pr´avˇe takov´a pravidla a fakta, kter´a si my sami definujeme. Proto aby interpret´ator vˇedˇel, ˇze je kˇreˇcek zv´ıˇre, mus´ıme upravit z´apis predik´atu zvire/1 n´asledovnˇe: zvire(X) :- kocka(X); pes(X); kralik(X); krecek(X). Tedy pˇridat kˇreˇcka do v´ yˇctu zv´ıˇrat. Potom je vˇse v poˇr´adku. Mus´ıme si ale d´avat velk´ y pozor na souvislosti. V u ´vodu jsme si ˇrekli, ˇze Prolog je zaloˇzen na vyhodnocov´an´ı c´ıl˚ u, rekurzi a backtrackingu (tzv. zpˇetn´em chodu).
4.1
Vyhodnocov´ an´ı c´ıl˚ u
Vrat’me se k prvn´ımu pˇr´ıkladu t´eto pr´ace. Abychom vypsali na obrazovku text Ahoj, svˇete!“, vyuˇzili jsme k tomu predik´at write/1. Ten je navrˇzen ” tak, aby jeho vyhodnocen´ı vˇzdy uspˇelo. Vyps´an´ı atomu uvnitˇr predik´atu na obrazovku nen´ı pˇri vyhodnocen´ı c´ıle standardn´ı chov´an´ı, proto m˚ uˇzeme ˇr´ıci, ˇze je to vedlejˇs´ı efekt. Sami m˚ uˇzeme definovat takov´ y predik´at, kter´ y pˇri vyhodnocen´ı vyp´ıˇse libovoln´ y text. K tomu vyuˇzijeme pr´avˇe jiˇz existuj´ıc´ı predik´at write/1. Vytvoˇr´ıme dva predik´aty pozdrav, jeden nul´arn´ı a jeden s argumentem, a predik´at vypis pozdrav/0: pozdrav(Osloveni) :- write(’Ahoj ’), write(Osloveni). pozdrav :- pozdrav(lidi), nl, write(’(Obecny pozdrav)’). vypis_pozdrav :- write(’Prolog zdravi’), nl, pozdrav.
Interpret´ator vyhodnocuje c´ıle vˇzdy zleva doprava a prohled´av´a datab´azi shora dol˚ u. Proto z´aleˇz´ı na poˇrad´ı. Nejprve se kontroluje hlava predik´atu, n´aslednˇe arita a nakonec tˇelo. Na predik´atu pozdrav si vyzkouˇs´ıme, jak funguje pˇretˇeˇzov´an´ı. Argumentem urˇc´ıme, koho v pozdravu oslov´ıme, pokud argument neuvedeme, osloven´ı bude obecnˇe lidi“: ”
13
ˇ Cinnost interpret´atoru a standardn´ı predik´aty
Jak funguje backtracking?
| ?- pozdrav(’Pepo’). Ahoj Pepo yes | ?- vypis_pozdrav. Prolog zdravi Ahoj lidi (Obecny pozdrav) yes
Bl´ıˇze se pod´ıv´ame na vyhodnocen´ı c´ıle vypis pozdrav/0. Interpret´ator se ˇ ıkali jsme si, ˇze je to zabupokus´ı vyhodnotit write(’Prvni radka’). R´ dovan´ y predik´at a je navrˇzen tak, ˇze jeho vyhodnocen´ı vˇzdy uspˇeje. Dalˇs´ı predik´at v ˇrade je nl/0. To je tak´e zabudovan´ y predik´at, kter´ y na aktu´aln´ı v´ ystup vyp´ıˇse novou ˇra´dku a tak´e se ho vˇzdy podaˇr´ı vyhodnotit. Interpret´ator pokraˇcuje vol´an´ım predik´atu pozdrav/0. Ten jsme si sami definovali, takˇze interpret´ator vyhodnot´ı jeho tˇelo a zjevnˇe uspˇeje. Vyhodnocov´an´ı se dokonˇc´ı v´ ypisem textu na novou ˇr´adku. C´ıl byl vyhodncen v pˇr´ım´em chodu a nebylo tˇreba ˇz´adn´ ych dalˇs´ıch mechanism˚ u. Pod´ıvejme se ale, co se stane, kdyˇz uprav´ıme predik´at pozdrav/0 takto: pozdrav :- pozdrav(lidi), fail. Vyhodnocen´ı zabudovan´eho predik´atu fail/0 je vˇzdy ne´ uspˇeˇsn´e, tedy umˇele urˇc´ıme, ˇze predik´at pozdrav/0 bude tak´e vˇzdy ne´ uspˇeˇsn´ y. V okamˇzik, kdy nˇekter´ y z posloupnosti predik´at˚ u je ne´ uspˇeˇsnˇe vyhodnocen, pˇrich´az´ı na ˇradu backtracking.
4.2
Jak funguje backtracking?
ˇ y v´ Cesk´ yznam slova backtracking je zpˇetn´ y chod“. Pokud m´a interpret´ator ” na v´ ybˇer v´ıce moˇznost´ı vyhodnocen´ı predik´atu, vˇzdy vybere prvn´ı moˇznost. Pokud se nepodaˇr´ı vyhodnotit tuto ˇc´ast posloupnosti, zkus´ı pouˇz´ıt jin´e ˇreˇsen´ı, kdyˇz existuje. Jestliˇze dalˇs´ı ˇreˇsen´ı neexistuje, vr´at´ı se od tohoto m´ısta o jeden krok zpˇet a postup opakuje – postupuje zprava doleva. Pˇrid´ame si jeˇstˇe jeden predik´at pro pozdrav pro n´azornou uk´azku backtrackingu. Nyn´ı m´ame definov´ana tato pravidla:
14
ˇ Cinnost interpret´atoru a standardn´ı predik´aty
Unifikace
pozdrav(Osloveni) :- write(’Ahoj ’), write(Osloveni). pozdrav :- pozdrav(lidi), nl, write(’(Obecny pozdrav)’), fail. % zachyceni chyby predikatu pozdrav/0 pozdrav :- write(’Pozdrav se nezdaril’). vypis_pozdrav :- write(’Prolog zdravi’), nl, pozdrav.
Pod´ıvejme se na vyhodnocen´ı predik´atu vypis pozdrav/0: | ?- vypis_pozdrav. Prolog zdravi Ahoj lidi (Obecny pozdrav) Pozdrav se nezdaril yes
Aˇz do predik´atu pozdrav/1 (vˇcetnˇe), kter´ y se vol´a z pozdrav/0, je vˇse stejn´e jako pˇri pˇredchoz´ım vyhodnocen´ı. Zmˇena je v n´asleduj´ıc´ım predik´atu fail/0, kter´ y neuspˇeje. Interpret´ator se vr´at´ı zpˇet na predik´at write/1, kter´ y se pˇri zpˇetn´em chodu nikdy nevyhodnot´ı kladnˇe a znovu nic nevyp´ıˇse, stejnˇe jako nl/0. D´ale interpret´ator pokraˇcuje na pozdrav(lidi) a zkus´ı naj´ıt jinou moˇznost ˇreˇsen´ı nebo jinou definici takov´eho pravidla. ˇ adn´a jin´a moˇznost nen´ı, proto vystoup´ı zpˇet tam, odkud vyhodnocov´an´ı Z´ zaˇcalo, tedy do tˇela struktury vypis pozdrav/0. Interpret´ator zkus´ı vyhledat dalˇs´ı v´ yskyty predik´atu pozdrav/0. Ten nalezne a pˇri vyhodnocov´an´ı uspˇeje s v´ ypisem Pozdrav se nezdaril“ a cel´e vyhodnocen´ı je u ´spˇeˇsn´e, proto je ” koneˇcn´a odpovˇed’ yes. Abychom mohli pokroˇcit d´ale, vysvˇetl´ıme si nejd˚ uleˇzitˇejˇs´ı funkci interpret´atoru – unifikaci.
4.3
Unifikace
Pouˇz´ıv´a se pˇri vyhodnocov´an´ı c´ıle s promˇenn´ ymi. Uk´aˇzeme si to konkr´etnˇe na programu Mazlicci.pro. Budeme cht´ıt napˇr´ıklad vidˇet jm´ena zv´ıˇrat v datab´azi. Pouˇzijeme n´asleduj´ıc´ı dotaz: zvire(Zvire). Zvire = micka ?
Programov´ y segment 4.1: Pouˇzit´ı promˇenn´e
15
ˇ Cinnost interpret´atoru a standardn´ı predik´aty
ˇ ızen´ı backtrackingu R´
Vˇsimnˇeme si pouˇzit´ı promˇenn´e Zvire. Ta zastupuje mezi argumenty (nyn´ı je pouze jeden) konkr´etn´ı atom. V naˇsem pˇr´ıpadˇe je promˇenn´a v dotazu nepˇriˇrazen´a – nem´a ˇza´dnou v´ ychoz´ı hodnotu. Zde zafunguje pr´avˇe proces unifikace. Je to jin´ ymi slovy pˇriˇrazen´ı do pr´azdn´e promˇenn´e tak, aby byly dva termy shodn´e. Slouˇz´ı pˇri vyhled´av´an´ı predik´atu v datab´azi Prologu [2]. Interpret´ator prohled´av´a datab´azi shora dol˚ u a hled´a predik´at zvire s aritou 1. Ten m´ame definovan´ y pouze jednou jako pravidlo, jehoˇz hlava je zvire(X). Aby byly predik´aty shodn´e, pˇriˇrad´ı se promˇenn´e Zvire pr´azdn´a promˇenn´a X. N´aslednˇe se vyhodnocuje tˇelo pravidla zleva doprava. Prvn´ı je uveden predik´at kocka(X). Promˇenn´a X st´ale nem´a ˇza´dnou hodnotu a hled´a se predik´at kocka/1 v datab´azi shora dol˚ u. Prvn´ı je nalezen fakt kocka(micka) a aby byl shodn´ y s kocka(X), mus´ı platit X = micka. Je to pouze fakt, proto vyhodnocen´ı uspˇeje a kladn´a odpovˇed’ se vrac´ı zpˇet do tˇela predik´atu zvire/1. ˇ adn´e dalˇs´ı predik´aty jiˇz vyhodnocovat nemus´ıme (ty jsou za stˇredn´ıkem, Z´ tedy voliteln´e), proto i cel´e pravidlo uspˇeje. ˇ adn´e dalˇs´ı n´avaznosti Nyn´ı jiˇz m´ame pˇriˇrazen´ı Zvire = X = micka. Z´ u c´ıle zvire(Zvire) nebyly, c´ıl je tedy vyhodnocen cel´ y. Pokud jsme s v´ ysledkem spokojeni, potvrd´ıme kl´avesou enter, interpret´ator odpov´ı yes a jsme zpˇet v interaktivn´ım uˇzivatelsk´em reˇzimu. My se ale s takovou odpovˇed´ı nespokoj´ıme a pod´ıv´ame se na to, jak ˇr´ıdit backtracking.
4.4
ˇ ızen´ı backtrackingu R´
Vrat’me se zpˇet k uk´azce k´odu 4.1. Ze z´apisu v´ıme, ˇze predik´at zvire/1 jistˇe splˇ nuje v´ıce mazl´ıˇck˚ u v datab´azi definovan´e programem 3.1. Pro nalezen´ı dalˇs´ı moˇznosti pouˇzijeme stˇredn´ık a potvrd´ıme kl´avesou enter. Pˇriˇrazen´ı promˇenn´e se v pr˚ ubˇehu cel´eho procesu zmˇen´ı na mnauka a znovu se vyp´ıˇse. V praxi to vypad´a takto: zvire(Zvire). Zvire = micka ?; Zvire = mnauka ?; Zvire = andy ?; Zvire = alik ?
Samozˇrejmˇe se postupnˇe vyp´ıˇsou vˇsechna jm´ena zv´ıˇrat. Vypad´a to jednoduˇse, ale v interpret´atoru je pod t´ım skryt ponˇekud sloˇzitˇejˇs´ı proces.
16
ˇ Cinnost interpret´atoru a standardn´ı predik´aty
ˇ ızen´ı backtrackingu R´
Stˇredn´ıkem ˇrekneme interpret´atoru, ˇze posledn´ı pˇriˇrazen´ı do promˇenn´e, tedy micka, nen´ı spr´avn´e a t´ım zaˇr´ıd´ıme, ˇze posledn´ı vyhodnocen´ y predik´at v ˇradˇe neuspˇeje. Konkr´etnˇe to byl predik´at kocka(micka). Proces se vr´at´ı opˇet aˇz do tˇela pravidla s hlavou zvire(X) s t´ım, ˇze promˇenn´a X je nyn´ı opˇet nepˇriˇrazena, a pokouˇs´ı se naj´ıt dalˇs´ı v´ yskyt predik´atu kocka/1. Ten nalezne, uspˇeje, pˇriˇrad´ı promˇennou Zvire = X = mnauka a vyp´ıˇse. Po tˇret´ım opakov´an´ı jiˇz nenalezne ani dalˇs´ı predik´at kocka/1, ale st´ale v tˇele n´asleduje za stˇredn´ıkem voliteln´a podm´ınka pes(X), kterou se povede vyhodnotit. Takto m˚ uˇzeme proj´ıt vˇsechna zv´ıˇrata a u posledn´ıho v´ yskytu interpret´ator automaticky vyhodnocov´an´ı ukonˇc´ı s kladnou odpovˇed´ı. Promˇennou m˚ uˇzeme pˇred vol´an´ım jeˇstˇe pˇriˇradit. To b´ yv´a nˇekdy zdrojem chyb v programu. Poloˇz´ıme napˇr´ıklad takov´ yto dotaz: | ?- X = tony, zvire(X). X = tony yes
Vyhodnocen´ı je stejn´e, ale do pravidla jiˇz vstupuje definovan´a promˇenn´a, proto se unifikuje odpov´ıdaj´ıc´ı promˇenn´a v jeho tˇele. Stejnˇe pro tento pˇr´ıpad funguje i dotaz zvire(tony). Kdyˇz budeme cht´ıt zjistit, kter´a zv´ıˇrata jsou b´ yloˇzravci, pouˇzijeme n´asleduj´ıc´ı dotaz: bylozravec(Zvire). Zvire = ferda ?; Zvire = matilda ?; no
Abychom vidˇeli, jak interpret´ator postupuje, potˇrebujeme se pod´ıvat, jak je definov´an predik´at bylozravec/1. Z tˇela se vyhodnocuj´ı jeˇstˇe predik´aty zere byli/1 a zere maso/1: bylozravec(X) :- zere_byli(X), not zere_maso(X). zere_maso(X) :- pes(X); kocka(X); krecek(X). zere_byli(X) :- kralik(X); krecek(X).
Vyhodnocov´an´ı prob´ıh´a totoˇznˇe s pˇredchoz´ım pˇr´ıkladem. Jelikoˇz jsou pravidla sloˇzitˇejˇs´ı, interpret´ator dopˇredu nev´ı, jestli je matilda posledn´ı moˇznost a jeˇstˇe ˇcek´a na interakci od uˇzivatele. Chceme-li vyvolat dalˇs´ı backtracking, c´ıl se jiˇz vyhodnotit nepovede a dostaneme negativn´ı odpovˇed’. 17
ˇ Cinnost interpret´atoru a standardn´ı predik´aty
4.4.1
ˇ ızen´ı backtrackingu R´
ˇ ızen´ı v pravidlech R´
Backtracking je pro n´as velice uˇziteˇcn´ y, ale nˇekdy n´am dok´aˇze pˇridˇelat vr´asky. Proto potˇrebujeme m´ıt moˇznost jej nˇejak´ ym zp˚ usobem kontrolovat i pˇri deˇ finov´an´ı pravidel. Reknˇeme, ˇze vˇsechna zv´ıˇrata maj´ı srst, aˇz na Besi, kter´a je nah´aˇc. Definujeme n´asleduj´ıc´ı pravidla: % Vlastnosti ma_srst(besi) :- fail. ma_srst(X) :- zvire(X).
Kdyˇz naˇcteme program do datab´aze a zad´ame c´ıl ma_srst(besi), dostaneme pˇresto kladnou odpovˇed’. To zapˇr´ıˇcinil proces zpˇetn´eho chodu, kter´ y po ne´ uspˇechu unifikoval c´ıl s n´asleduj´ıc´ım predik´atem ma srst/1, jehoˇz tˇelo Besi splˇ nuje. Pro zabr´anˇen´ı backtrackingu vyuˇzijeme predik´at !/0 nazvan´ y cut neboli volnˇe pˇreloˇzeno ˇrez“. S v´ yhodou jej vyuˇzijeme jeˇstˇe v n´asleduj´ıc´ı ˇc´asti pro ” ˇ pouˇzijeme definov´an´ı poˇcetn´ıch funkc´ı, ale i v mnoha dalˇs´ıch pˇr´ıpadech. Rez n´asledovnˇe: ma_srst(besi) :- !, fail. ma_srst(X) :- zvire(X).
Pro konkr´etn´ı dotazy nyn´ı interpret´ator reaguje spr´avnˇe: | ?- ma_srst(besi). no | ?- ma_srst(micka). yes
Z´apisu !, fail se ˇr´ık´a cut with failure“. Bohuˇzel ani takov´e oˇsetˇren´ı ” nefunguje ve vˇsech pˇr´ıpadech spr´avnˇe. Poloˇzme obecn´ y dotaz kdo m´a srst?“: ” | ?- ma_srst(X). no
Negativn´ı vyhodnocen´ı nastane proto, ˇze interpret´ator unifikuje promˇennou X jako prvn´ı s atomem besi, kter´ y skonˇc´ı s chybou a zpˇetn´ y chod jsme ˇrezem zak´azali. Lepˇs´ı pˇr´ıstup je takov´ y, jak´ y jsme si jiˇz uk´azali u potravinov´ ych strategi´ı. Definujeme si nov´ y predik´at nahac/1 a budeme tvrdit, ˇze srst m´a zv´ıˇre, kter´e nen´ı nah´aˇc. Takto bude vypadat upraven´a ˇc´ast:
18
ˇ Cinnost interpret´atoru a standardn´ı predik´aty
N´avratov´a hodnota
% Vlastnosti nahac(besi). ma_srst(X) :- zvire(X), \+ nahac(X).
Nyn´ı kdyˇz zkus´ıme vyhodnotit c´ıl ma_srst(X), dostaneme spr´avn´e v´ ysledky. V k´odu vid´ıme nov´ y predik´at (oper´ator) \+/1. Ten m´a stejnou funkci jako not, kter´ y jsme si jiˇz vysvˇetlovali v pˇr´ıkladu 3.2.
4.5
N´ avratov´ a hodnota
Jako napˇr. v jazyce C m´ame funkce, kter´e n´am vracej´ı po proveden´ı hodnotu dan´eho typu (chybov´ y k´od apod.), m˚ uˇzeme podobnˇe z´ıskat n´avratovou hodnotu z vyhodnocen´ı predik´atu. Pˇrid´ame si dva nov´e predik´aty do programu Mazlicci.pro: dlouhe_usi(X) :- kralik(X). dlouhe_usi(rony). dlouhe_usi(alik). vzhled_zvirete(Zvire, chlupate) :- ma_srst(Zvire). vzhled_zvirete(Zvire, bez_srsti) :- nahac(Zvire). vzhled_zvirete(Zvire, usate) :- dlouhe_usi(Zvire).
Pˇri definov´an´ı predik´atu nem˚ uˇzeme nijak explicitnˇe urˇcit n´avratovou hodnotu, proto je predik´at bin´arn´ı a n´avratovou hodnotu pˇredstavuje druh´ y argument. Nyn´ı program opˇet naˇcteme a vyzkouˇs´ıme funkˇcnost. Kdyˇz chceme vˇedˇet, jak vypad´a Matilda, poloˇz´ıme dotaz t´ımto zp˚ usobem: | ?- vzhled_zvirete(matilda, X). X = chlupate ?; X = usate ? yes
S takto definovan´ ym predik´atem tak´e m˚ uˇzeme zadat poˇzadovanou n´avratovou hodnotu a pt´at se na vyhovuj´ıc´ı zv´ıˇrata. Takov´e chov´an´ı n´am umoˇzn ˇuje backtracking spoleˇcnˇe s unifikac´ı. Jsou to procesy, kter´e v Prologu funguj´ı automaticky vˇsude a nemus´ıme je popisovat ˇza´dn´ ym algoritmem, jak bychom to museli udˇelat v jazyce Java. N´avratov´e hodnoty vyuˇzijeme hojnˇe napˇr. pˇri definov´an´ı matematick´ ych operac´ı jako napˇr´ıklad porovn´av´an´ı dvou hodnot: vetsi_cislo(Vetsi, Mensi, Vetsi) :- Vetsi > Mensi, !. vetsi_cislo(_, Vetsi, Vetsi).
19
ˇ Cinnost interpret´atoru a standardn´ı predik´aty
N´avratov´a hodnota
Tento pˇr´ıklad bude fungovat spr´avnˇe. Jako tˇret´ı argument zad´ame v dotazu nedefinovanou promˇennou, do kter´e je pˇred´ano vˇetˇs´ı ˇc´ıslo. Vˇsimnˇeme si pˇri t´eto pˇr´ıleˇzitosti pouˇzit´ı ˇrezu. Kdybychom jej vynechali, m˚ uˇze nastat n´asleduj´ıc´ı probl´em [2]: | ?- vetsi_cislo(8, 5, X). X = 8 ?; X = 5 yes
V programu se m˚ uˇzeme pt´at i na konkr´etnˇe definovan´e ˇc´ıslo, kter´e si mysl´ıme, ˇze je vˇetˇs´ı: | ?- vetsi_cislo(8, 5, 8). yes
Takto poloˇzen´ y dotaz bez promˇenn´ ych se u ´spˇeˇsnˇe unifikuje se zadan´ ym pravidlem vetsi_cislo(X, Y, X). V tomto pˇr´ıpadˇe by se ale interpret´ator vyhodnotil kladnˇe i dotaz: | ?- vetsi_cislo(8, 5, 5). yes
Proto nebudeme spol´ehat na postupn´e vyhodnocov´an´ı, protoˇze mohou nastat pˇr´ıpady, kdy se bude chovat program jinak, neˇz si pˇredstavujeme. Radˇeji budeme pˇrid´avat do pravidel kontroly nav´ıc, abychom zajistili robustnost ˇreˇsen´ı.
20
5 Standard ISO Prolog ˇ ast standardn´ıch zabudovan´ C´ ych predik´at˚ u jsme si jiˇz pˇredstavili v pˇredchoz´ıch kapitol´ach, ale st´ale existuje hodnˇe zabudovan´ ych predik´at˚ u, kter´e by se n´am v dalˇs´ı pr´aci mohly hodit. Kompletn´ı seznam najdeme v pˇr´ıloze A knihy Prolog Programming in Depth od M. Covingtona [3]. Uk´aˇzeme si zde na pˇr´ıkladech pro n´as nejzaj´ımavˇejˇs´ı.
5.1
Oper´ atory
Kromˇe standardn´ıch struktur se v Prologu setk´ame i s oper´atory. Ty se pouˇz´ıvaj´ı nejen pro numerick´e operace, ale m˚ uˇzeme si definovat i vlastn´ı s poˇzadovanou funkc´ı. Zavedeny jsou proto, abychom napˇr´ıklad operaci sˇc´ıt´an´ı nemuseli zapisovat ve funktorov´e notaci, tedy +(4, 7), ale mohli jsme pouˇz´ıt pˇrirozenˇejˇs´ı z´apis 4 + 7. Existuj´ı tˇri druhy oper´ator˚ u: Prefixov´ e – jejich z´astupcem je predik´at not/1. P´ıˇse se vˇzdy pˇred term, kter´ y pˇredstavuje jeho argument. Napˇr´ıklad not zere_maso(X). Infixov´ e – jsou vˇsechny bin´arn´ı oper´atory. Zapisuj´ı se mezi dva termy, kter´e pˇredstavuj´ı jeho argumenty. Je to napˇr. opr´ator disjunkce (,)/2 nebo matematick´ ych operac´ı jako sˇc´ıt´an´ı (+)/2 apod. Postfixov´ e – p´ıˇs´ı se za argument, vyskytuj´ı se velice zˇr´ıdka. Zkusme nyn´ı vyhodnotit c´ıl 5+3.: | ?- 5+3. *** Undefined procedure: (+)/2
V´ yjimka nastane proto, ˇze oper´ator + nen´ı urˇcen pro unifikaci. Aby mohl interpret´ator unifikaci pouˇz´ıt, naskytne se n´am pouˇzit´ı promˇenn´e. Zkus´ıme poloˇzit dotaz takto: | ?- X = 5 + 3. X = 5+3 yes
Prolog v tomto pˇr´ıpadˇe zafungoval spr´avnˇe, tedy pˇriˇradil do promˇenn´e X predik´at (+)/2. Je to sten´e, jako bychom promˇenn´e pˇriˇradili napˇr. strukturu krecek(tony) nebo atom. Aby se vyhodnotily aritmetick´e operace, mus´ıme pouˇz´ıt zabudovan´ y infixov´ y oper´ator is/2, a to n´asledovnˇe: 21
Standard ISO Prolog
Oper´atory
| ?- X is 5 + 3. X = 8 yes
Za is m˚ uˇzeme ps´at jak´ekoliv sloˇzit´e rovnice, bohuˇzel ale nem˚ uˇzeme pouˇz´ıvat nedefinovan´e promˇenn´e. Jak aproximovat v´ ysledek sloˇzit´e rovnice s nezn´am´ ymi napsal M. Covington ve sv´em v´ yzkumu [4]. N´am bude prozat´ım staˇcit z´akladn´ı pr´ace s oper´atory.
5.1.1
Definice vlastn´ıch oper´ ator˚ u
Jak jiˇz bylo ˇreˇceno, je moˇzn´e definovat si vlastn´ı oper´atory. Pro uk´azku si vytvoˇr´ıme do souboru Osoby.pro jednoduchou datab´azi osob, kter´e jsou spoleˇcnˇe ve vztahu. V dalˇs´ıch kapitol´ach program rozˇs´ıˇr´ıme: % Muzi muz(jan). muz(roman). muz(ales). % Zeny zena(stela). zena(ema). zena(lucie). zena(petra). % Vztahy manzele(jan, stela). manzele(ales, petra). miluje(X, Y) :- manzele(X, Y).
V naˇsem jednoduch´em svˇetˇe jsou tˇri muˇzi a ˇctyˇri ˇzeny, z toho dva p´ary jsou v manˇzelsk´em vztahu. Tvrd´ıme, ˇze manˇzel´e se navz´ajem miluj´ı. M˚ uˇzeme poloˇzit nˇekolik dotaz˚ u: | ?- miluje(jan, stela). yes | ?- miluje(X, petra). X = ales yes
Ovˇsem pˇrirozenˇejˇs´ı z´apis by byl napˇr. jan miluje stela. To se d´a zaˇr´ıdit pomoc´ı zabudovan´eho predik´atu op/3. Ten se zapisuje ve tvaru: :- op(priorita, specifikator, nazev_operatoru) Priorita je cel´e ˇc´ıslo od nuly v´ yˇse a znamen´a, v jak´em poˇrad´ı se bude opeˇ ım vyˇsˇs´ı ˇc´ıslo, t´ım niˇzˇs´ı priorita, tedy r´ator vyhodnocovat pˇred ostatn´ımi. C´ 22
Standard ISO Prolog
Probl´em symetrick´ych relac´ı
pozdˇejˇs´ı vyhodnocen´ı. Napˇr´ıklad n´asoben´ı m´a prioritu 400, sˇc´ıt´an´ı a odˇc´ıt´an´ı 500 apod. Specifik´ator urˇcuje asociativitu a zda bude oper´ator prefixov´ y, infixov´ y nebo postfixov´ y. Asociativita urˇcuje, z jak´e strany se z´apisy vyhodnocuj´ı. Specifik´ator se skl´ad´a z urˇcen´e posloupnost znak˚ u (atom): Atom fx fy xfx xfy yfx xf yf
Notace prefixov´a prefixov´a infixov´a infixov´a infixov´a postfixov´a postfixov´a
Asociativita neasociativn´ı asociativn´ı zprava neasociativn´ı asociativn´ı zprava asociativn´ı zleva neasociativn´ı asociativn´ı zleva
Oper´ator miluje/2 definujeme bez asociativity jako infixovou notaci, protoˇze je to p˚ uvodnˇe bin´arn´ı predik´at. Definovat funktor jako oper´ator mus´ıme pˇred jeho prvn´ım pouˇzit´ım v programu n´asledovnˇe: ?- op(200, xfx, miluje). I v k´odu mus´ı b´ yt na zaˇca´tku ˇra´dky zaps´any znaky ?-, jimiˇz zaˇc´ın´a v´ yzva v uˇzivatelsk´em reˇzimu, event. znaky :-. Tomuto z´apisu se ˇr´ık´a direktiva [2]. Predik´at miluje/2 m˚ uˇzeme pouˇz´ıvat d´ale libovolnˇe i ve funktorov´em z´apisu.
5.2
Probl´ em symetrick´ ych relac´ı
Zkus´ıme zadat dotaz za pomoci oper´atoru a uk´aˇzeme si nedostatek v aktua´ln´ım programu: | ?- ales miluje X. X = petra yes | ?- stela miluje X. no
Odpovˇed’ na druh´ y dotaz je negativn´ı, protoˇze jsme definovali tˇelo pravidla miluje pouze jednostrannˇe. Aby pravidlo platilo symetricky, definujeme nov´ y predik´at chot/2 a uprav´ıme pravidlo n´asleduj´ıc´ım zp˚ usobem: 23
Standard ISO Prolog
Probl´em symetrick´ych relac´ı
chot(X, Y) :- manzele(X, Y); manzele(Y, X). miluje(X, Y) :- chot(X, Y).
S touto symetrickou realc´ı bude Prolog vyhodnocovat pˇredchoz´ı dotazy spr´avnˇe. Pokud ale poloˇz´ıme obecn´ y dotaz chot(X, Y)., lid´e se pˇri backtrackingu budou opakovat. | ?- chot(X, Y). X = jan Y = stela ?; X = ales Y = petra ?; X = stela Y = jan ?; X = petra Y = ales yes
Tento probl´em se objevuje u kaˇzd´e symetrick´e relace. Vhodn´e ˇreˇsen´ı m˚ uˇze b´ yt v r˚ uzn´ ych pˇr´ıpadech individu´aln´ı a neexistuje ˇza´dn´e univerz´aln´ı pravidlo. V naˇsem pˇr´ıpadˇe staˇc´ı ˇr´ıci, ˇze symetrick´e ˇreˇsen´ı chceme hledat pouze tehdy, kdyˇz neexistuje ˇreˇsen´ı p˚ uvodn´ı. K tomu pouˇzijeme nov´ y z´apis – podm´ınku.
5.2.1
Podm´ınka
Uprav´ıme predik´at chot n´asleduj´ıc´ım zp˚ usobem: chot(X, Y) :- \+ manzele(X, Y) -> manzele(Y, X) ; manzele(X, Y). Z´apis predikat1 -> predikat2 ; predikat3 je podm´ınka if“ jak ji ” zn´ame z jin´ ych programovac´ıch jazyk˚ u. Prvn´ı se vyhodnot´ı predikat1, pokud uspˇeje, zavol´a se predikat2, v opaˇcn´em pˇr´ıpadˇe se zavol´a predikat3 – ten je ale v z´apisu nepovinn´ y. Pokud neuspˇeje predikat2, jako dalˇs´ı se predikat3 vyhodnocovat nebude kv˚ uli z´avislosti implikace -> [2]. Ani ted’ bohuˇzel nen´ı ˇreˇsen´ı ide´aln´ı pro vˇsechny pˇr´ıpady. Pod´ıvejme se na n´asleduj´ıc´ı dotazy: | ?- Y = ales, X miluje Y. Y = ales X = petra yes | ?- X miluje Y, Y = ales. no
24
Standard ISO Prolog
Probl´em symetrick´ych relac´ı
Dalˇs´ı probl´em je symetriˇcnost u dotazu X miluje Y., jelikoˇz je v´ yˇcet interpretov´an pouze jednostrannˇe, i kdyˇz chceme, aby v naˇsem programu byl tento vztah symetrick´ y. U deklarativn´ıho programov´an´ı nˇekdy nelze jednoduˇse pokr´ yt vˇsechny moˇznosti zadan´ ych dotaz˚ u, ale mˇeli bychom se soustˇredit hlavnˇe na poˇzadovanou funkˇcnost.
5.2.2
Abstrakce
Na z´akladˇe jiˇz existuj´ıc´ıch a dobˇre navrˇzen´ ych fakt˚ u a pravidel lze vˇzdy snadno pˇridat dalˇs´ı funkcionalitu. Pˇri psan´ı program˚ u za pomoci klasick´ ych i deklarativn´ıch jazyk˚ u bychom mˇeli udrˇzovat vhodnou m´ıru abstrakce. U Prologu abstrakci vyuˇzijeme pro zjednoduˇsen´ı sloˇzit´ ych pravidel. Kaˇzd´ y velk´ y probl´em se skl´ad´a z ˇc´ast´ı, ale z´aleˇz´ı na n´as, jak drobn´e ˇca´sti se budou vyskytovat na jednom m´ıstˇe. Dekompozice by na vysok´e u ´rovni nemˇela b´ yt u ´pln´a, ale mˇela by b´ yt rozvrˇzena do v´ıce ˇca´st´ı. To pˇrispˇeje ˇcitelnosti k´odu a pˇr´ıpadnˇe n´asledn´e pouˇzitelnosti ˇca´st´ı v jin´ ych pravidlech. Budeme se snaˇzit ps´at k´od ˇcistˇe a jednoduˇse. Pro uk´azku zad´ame nov´a fakta: % Muzi muz(jan). muz(roman). muz(ales). muz(michal). muz(josef). % Zeny zena(stela). zena(ema). zena(lucie). zena(petra). % Vztahy manzele(jan, stela). manzele(roman, ema). manzele(ales, petra). milenci(roman, petra). milenci(lucie, ales).
25
Standard ISO Prolog
Vstup od uˇzivatele
A definujeme rozˇsiˇruj´ıc´ı pravidla vztah˚ u za pouˇzit´ı doposud z´ıskan´ ych znalost´ı: % Obecna pravidla chot(X, Y) :- \+ manzele(X, Y) -> manzele(Y, X) ; manzele(X, Y). v_mileneckem_vztahu(X, Y) :- \+ milenci(X, Y) -> milenci(Y, X); milenci(X, Y). ?- op(200, xfx, podvadi_s). X podvadi_s Y :- chot(X, Z), v_mileneckem_vztahu(X, Y). podvadejici(X) :- X podvadi_s _. ?- op(200, xfx, podvadi). X podvadi Chot :- chot(X, Chot), podvadejici(X). ?- op(200, xfx, miluje(X, Y) :miluje(X, Y) :miluje(X, Y) :-
miluje). milenci(X, Y) ; milenci(Y, X). manzele(X, Y), \+ podvadejici(X). manzele(Y, X), \+ podvadejici(X).
Pro program takto mal´eho rozsahu je nyn´ı dekompozice dostateˇcn´a. Vˇsimnˇeme si pouˇzit´ı negac´ı, podm´ınek, anonymn´ı promˇenn´e, definic oper´ator˚ u atp. Je dobr´e pouˇz´ıvat korektnˇejˇs´ı n´azvy promˇenn´ ych neˇz jen X a Y. U bin´arn´ıch predik´at˚ u to ale vˇetˇsinou nen´ı potˇreba, protoˇze je n´am z kontextu z´apisu jasn´e, co promˇenn´e znamenaj´ı.
5.3
Vstup od uˇ zivatele
Jestliˇze vytv´aˇr´ıme program, ve kter´em chceme nechat uˇzivatele zadat libovoln´ y vstup, aniˇz by pro nˇej byly viditeln´e vnitˇrn´ı struktury, pouˇzijeme k tomu predik´at read/1. Jeho argumentem je promˇenn´a, ve kter´e bude n´aslednˇe pˇriˇrazen zadan´ y vstup. Predik´at si m˚ uˇzeme vyzkouˇset i v interaktivn´ım prostˇred´ı interpret´atoru: | ?- read(X), write(’napsal(a) jste ’), write(X), nl. | ahoj. napsal(a) jste ahoj X = ahoj yes
26
Standard ISO Prolog
Cyklus
M˚ uˇzeme napsat pouze term dle konvenc´ı jazyka Prolog (viz 3.1) ukonˇcen´ y teˇckou a nakonec potvrd´ıme kl´avesou enter. Abychom mohli napsat libovoln´ y ˇretˇezec znak˚ u bez znalosti konvenc´ı, definujeme si v kapitole 5.7 predik´at, kter´ y to bude umoˇzn ˇovat.
5.4
Cyklus
Cykly jako while nebo for, jak je zn´ame z klasick´ ych programovac´ıch jazyk˚ u, v Prologu nenajdeme. K vytvoˇren´ı cyklu vyuˇzijeme backtracking a zabudovan´ y predik´at repeat/0. Ten pˇri pˇr´ım´em vyhodnocen´ı uspˇeje a pˇri zpˇetn´em chodu tak´e. Pˇred tento predik´at se backtrackingem jiˇz nedostaneme a program se chov´a, jako kdyby naˇsel dalˇs´ı moˇzn´e ˇreˇsen´ı. Po pouˇzit´ı repeat mus´ı existovat moˇznost, jak c´ıl splnit, nebo se bude cyklus opakovat donekoneˇcna. Vytvoˇr´ıme si pro uk´azku jednoduch´ y kv´ız: kviz :- write(’Vitejte v pocetnim kvizu!’), nl, otazka. otazka :- write(’Kolik je 5+3?’), nl, repeat, read(X), odpoved(X). odpoved(8) :- write(’Spravna odpoved, dekuji’). odpoved(X) :- write(’Odpoved ’), write(X), write(’ je chybna. Zkuste to prosim znovu’), nl, fail.
Pro lepˇs´ı pochopen´ı se pod´ıv´ame rovnou na funkci tohoto programu: | ?- kviz Vitejte v pocetnim kvizu! Kolik je 5+3? | 6. Odpoved 6 je chybna. Zkuste to prosim znovu | 8. Spravna odpoved, dekuji yes
Startovac´ı bod je predik´at kviz/0. Vid´ıme v´ ypis uv´ıt´an´ı a vol´an´ı predik´atu otazka/0. Ten v tˇele obsahuje samotnou ot´azku a predik´at repeat/0, kter´ y pˇredstavuje cyklus pˇri backtrackingu. N´asleduje naˇcten´ı ze standardn´ıho vstupu do promˇenn´e X a kontrola tohoto vstupu. Ot´azka je jasnˇe poloˇzena a m´a pr´avˇe jednu moˇznost odpovˇedi. Proto staˇc´ı napevno definovat atom spr´avn´e odpovˇedi. V pˇr´ıpadˇe, ˇze je odpovˇed’ jin´a, vyp´ıˇseme na obrazovku upozornˇen´ı a zajist´ıme nesplnˇen´ı c´ıle predik´atem 27
Standard ISO Prolog
Rekurze
fail. T´ım nebude splnˇen ani c´ıl odpoved(X), read pˇri backtrackingu neuspˇeje, ale na predik´atu repeat se backtracking zastav´ı a pokraˇcujeme opˇet pˇr´ım´ ym chodem od zad´an´ı hodnoty.
5.5
Rekurze
Kv´ız v pˇredchoz´ım pˇr´ıkladu je velice primitivn´ı a zaslouˇz´ı si rozˇs´ıˇren´ı. Obohat´ıme jej o cyklus, kter´ y je ˇreˇsen za pomoci jednoduch´e rekurze, a pˇrid´ame dynamiˇcnost: kviz :write(’Vitejte v pocetnim kvizu! Ukoncite jej slovem ’’konec’’’), nl, otazka(1, 1). otazka(C1, C2) :write(’Kolik je ’), write(C1), write(’ + ’), write(C2), write(’?’), nl, repeat, read(X), Vysledek is (C1+C2), odpoved(X, Vysledek). odpoved(konec, _) :- write(’Dekuji za spolupraci, nashledanou.’). odpoved(X, Vysledek) :(X == Vysledek) -> write(’Spravna odpoved, dekuji’), nl, Z is ceiling(X*1.3), otazka(X, Z). odpoved(X, _) :write(’Odpoved ’), write(X), write(’ je chybna. Zkuste to prosim znovu’), nl, fail.
Jak kv´ız funguje si jiˇz urˇcitˇe dok´aˇzeme pˇredstavit. Novinka je pˇrepoˇc´ıt´av´an´ı nov´ ych hodnot k seˇcten´ı. Predik´atu odpoved/2 mus´ıme pˇred´avat mimo tipovanou hodnotu i hodnotu spr´avn´eho v´ ysledku, jelikoˇz bude v kaˇzd´em kole jin´a. Nov´e hodnoty vypoˇcteme aˇz po naps´an´ı spr´avn´eho v´ ysledku a vol´ame rekurzivnˇe predik´at otazka/2 s tˇemito hodnotami. Kv´ız bude aktivn´ı aˇz do naps´an´ı kl´ıˇcov´eho slova konec“. Pˇri chybn´e ” odpovˇedi se bude vracet backtrackingem aˇz k predik´atu repeat/0, pˇri spr´avn´e se bude rekurzivnˇe pokl´adat nov´a ot´azka. Zaj´ımavˇejˇs´ım pˇr´ıkladem pro rekurzi je poˇc´ıt´an´ı souˇctu vˇsech ˇc´ısel od nuly do dan´eho ˇc´ısla. Rekurze spoˇc´ıv´a v tom, ˇze pro poˇzadovanou sumu do ˇc´ısla N mus´ıme spoˇc´ıtat souˇcet do ˇc´ısla N − 1 a k tomu ˇc´ıslo N pˇriˇc´ıst. Pokud je 28
Standard ISO Prolog
Seznamy
ˇc´ıslo N = 1, nic nepˇriˇc´ıt´ame: suma_do(1, Vysledek) :- Vysledek is 1. suma_do(X, Vysledek) :C1 is X-1, suma_do(C1, C2), Vysledek is C2 + X, !.
Pokud bychom v druh´em pravidle nepouˇzili ˇrez, umoˇznili bychom vyvolat zpˇetn´ y chod a interpret´ator by rekurzivnˇe poˇc´ıtal z´aporn´a ˇc´ısla do nekoneˇcna.
5.6
Seznamy
Velice uˇziteˇcn´e jsou v prologu seznamy, neboli listy. V kapitole 3.1 jsme si jiˇz nast´ınili moˇznosti pouˇzit´ı a z´apis. Zde se pod´ıv´ame na problematiku podrobnˇeji vˇcetnˇe uk´azkov´ ych pˇr´ıklad˚ u. Z´apis v hranat´ ych z´avork´ach [a, b, c] je zjednoduˇsen´ y z´apis funktorov´e notace .(a, .(b, .(c, []))).
5.6.1
Pˇ rid´ av´ an´ı prvk˚ u
Jednou z moˇznost´ı, jak pˇrid´avat prvky do existuj´ıc´ıho seznamu je z´apis [termy | seznam], kde svisl´a ˇca´ra zastupuje konstruktor seznamu. Nalevo je vypsan´ y libovoln´ y poˇcet term˚ u a napravo je seznam, do kter´eho se maj´ı termy pˇridat: | ?- List = [5, 10, 25], X is 25 - 3, Y = sqrt(121), List2 = [X, Y | List]. List = [5,10,25] X = 22 Y = sqrt(121) List2 = [22,sqrt(121),5,10,25] yes
Jestliˇze chceme pˇridat prvky na konec seznamu, m˚ uˇzeme pouˇz´ıt zabudovan´ y predik´at append/3. Jako argumenty pˇrij´ım´a v´ yhradnˇe listy. Mus´ıme si d´avat pozor na pˇrid´an´ı jendoduch´eho atomu: | ?- X = 10, append([5, 10, 25], X, List). X = 10 List = [5,10,25|10] yes
Z´apis [5,10,25|10] je sice dle konvenc´ı spr´avn´ y, ale s takovou strukturou ˇ se d´ale ˇspatnˇe pracuje. Reˇsen´ım je vytvoˇren´ı jednoprvkov´eho seznamu: 29
Standard ISO Prolog
Seznamy
| ?- X = 10, append([5, 10, 25], [X], List). X = 10 List = [5,10,25,10] yes
Predik´at append vyuˇz´ıv´a vnitˇrnˇe konstruktor seznamu, proto stejn´eho v´ ysledku dos´ahneme i z´apisem: X = 10, List = [5, 10, 25|[X]].
5.6.2
Zpracov´ av´ an´ı prvk˚ u
Nejd˚ uleˇzitˇejˇs´ı ˇca´st pr´ace s prvky seznamu je jejich zpracov´an´ı. Z´aleˇz´ı, na co seznam vyuˇz´ıv´ame, ale postup je vˇetˇsinou ekvivalentn´ı. Jak jiˇz bylo ˇreˇceno, m˚ uˇzeme prov´est dekompozici seznamu na prvn´ı prvek (hlavu) a zbytek seznamu. Vyuˇzijeme unifikaci s pravidlem, kter´e jako argument pˇrij´ım´a z´apis [Hlava | Telo]. Jako nejjednoduˇsˇs´ı pˇr´ıklad si uk´aˇzeme v´ ypis vˇsech prvk˚ u seznamu pod sebe: vypis_prvky_seznamu(S) :- vypis_prvky_ocislovane(S, 1). vypis_prvky_ocislovane([], _). vypis_prvky_ocislovane([H|T], N) :write(N), write(’. prvek = ’), write(H), nl, N1 is N+1, vypis_prvky_ocislovane(T, N1).
Vyzkouˇs´ıme si navrˇzen´e ˇreˇsen´ı: | ?- vypis_prvky([prvni,[1,2],3]). 1. prvek = prvni 2. prvek = [1,2] 3. prvek = 3 yes
Pokud bychom nedefinovali predik´at vypis_prvky_ocislovane([], _), vyhodnocen´ı by se provedlo, ale odpovˇed’ by byla negativn´ı, jelikoˇz pr´azn´ y seznam by se s ˇza´dn´ ym predik´atem jiˇz neunifikoval. Hlava seznamu je vˇzdy prvn´ı term nepr´azdn´eho seznamu a zbytek neboli tˇelo p˚ uvodn´ıho seznamu je vˇzdy seznam bez prvn´ıho prvku, m˚ uˇze b´ yt tedy i pr´azdn´ y. Definujeme si s aktu´aln´ımi znalostmi jednoduchou operaci sˇc´ıt´an´ı ˇc´ıseln´ ych prvk˚ u seznamu. Vytvoˇr´ıme pro to prefixov´ y oper´ator +/1, kter´ y se nap´ıˇse pˇred seznam, kter´ y chceme seˇc´ıst.
30
ˇ ezce Retˇ
Standard ISO Prolog
V´ ysledek sˇc´ıt´an´ı potˇrebujeme pˇredat do promˇenn´e, tak n´am un´arn´ı predik´at staˇcit nebude. U numerick´e operace se n´am naskytne pouˇzit´ı oper´atoru is/2, jenˇze ten takov´ y zp˚ usob sˇc´ıt´an´ı nepodporuje. Zabudovan´e predik´aty nem˚ uˇzeme pˇredefinovat, proto si vytvoˇr´ıme vlastn´ı oper´ator je/2: ?-op(800, xfx, je). ?-op(200, fx, ’+’). X je +[X] :- !. X je +[H|T] :- Y je +T, X is Y + H.
Opˇet unifikujeme hlavu a tˇelo seznamu do zvl´aˇstn´ıch promˇenn´ ych. Dokud je v seznamu v´ıce neˇz jeden prvek, pouˇzijeme rekurzi. Posledn´ı prvek pˇriˇrad´ıme do promˇenn´e a pouˇzijeme ˇrez, protoˇze jin´e moˇznosti souˇctu ˇc´ısel neexistuj´ı. V´ ysledky rekurzivn´ıch vol´an´ı pˇriˇc´ıt´ame k souˇcasn´e hodnotˇe. T´ımto zp˚ usobem se pole seˇcte zprava doleva: | ?- X je +[-6,15+6,3*3,2**4,sqrt(36)]. X = 46.0 yes
Jak je vidˇet, v jednotliv´ ych prvc´ıch pole se mohou nach´azet i sloˇzitˇejˇs´ı matematick´e v´ yrazy, kter´e podporuje oper´ator is/2. Ekvivalentnˇe m˚ uˇzeme za pomoci nov´eho prdik´atu je definovat libovoln´e operace se seznamy nebo s rozˇs´ıˇren´e matematick´e operace. Dalˇs´ı vyuˇzit´ı m´a seznam pˇri z´apisu ˇretˇezc˚ u.
5.7
ˇ ezce Retˇ
Libovoln´e textov´e ˇretˇezce nejsou jenom obyˇcejn´e atomy, ale je to speci´aln´ı posloupnost znak˚ u uvozen´a dvojit´ ymi uvozovkami. Jako v jazyce C se ˇretˇezec reprezentuje pomoc´ı pole (seznamu) jednotliv´ ych znak˚ u a kaˇzd´ y znak m´a urˇcen´ y sv˚ uj ˇc´ıseln´ y k´od v ASCII tabulce: | ?- X = "Retezec" X = [82,101,116,101,122,101,99] yes
Dalˇs´ı uˇziteˇcen´ y zabudovan´ y predik´at je name/2. Ten slouˇz´ı k pˇrevodu atomu na ˇretˇezec reprezentuj´ıc´ı jeho n´azev a naopak. Smˇer pˇrevodu urˇc´ıme t´ım, do kter´eho z argument˚ u zad´ame nepˇriˇrazenou promˇennou. M˚ uˇzeme ˇretˇezce i porovn´avat: 31
ˇ ezce Retˇ
Standard ISO Prolog
| ?- name(’Retezec’, X). X = [82,101,116,101,122,101,99] yes | ?- name(X, [82,101,116,101,122,101,99]). X = ’Retezec’ yes | ?- name(’Retezec’, [82,101,116,101,122,101,99]). yes
S jeho pomoc´ı si vyrob´ıme predik´at cti radku/1, kter´ y bude ˇc´ıst libovoln´ y text ze vstupu aˇz do odˇra´dkov´an´ı, bez nutnosti uvozovek nebo ukonˇcen´ı teˇckou. Vytvoˇr´ıme si proto nov´ y soubor Vstup.pro: cti_radku(X) :- cti_znak_rekurzivne([], List), name(X, List), !. cti_znak_rekurzivne(Stary_list, Novy_list) :get0(X), zpracuj_znak(X, Stary_list, Novy_list). % Odchyceni odradkovani v Linuxu zpracuj_znak(10, Stary_list, Stary_list). zpracuj_znak(X, Stary_list, Novy_list) :append(Stary_list, [X], L), cti_znak_rekurzivne(L, Novy_list).
Pro spr´avnou funkci jsme vyuˇzili znalosti rekurze, ˇrezu i pr´ace s listy. Vid´ıme nov´ y zabudovan´ y predik´at get0/1, kter´ y ˇcte jeden libovoln´ y znak ze vstupu uˇzivatele. Predik´at get/1 funguje velice podobnˇe, ale vyuˇz´ıt bychom jej nemohli, jelikoˇz ignoruje vˇsechny b´ıl´e znaky“ neboli anglicky whitespace“. ” ” Mezi nˇe se ˇrad´ı merezy, odsazen´ı a hlavnˇe odˇr´adkov´an´ı, kter´e potˇrebujeme odchyt´avat. Takto n´aˇs predik´at funguje: | ?- cti_radku(X). Muzeme pouzit libovolne znaky jako treba: ’?:_-/\%"’ apod. X = ’Muzeme pouzit libovolne znaky jako treba: \’?:_-/\%"\’ apod.’ yes
Aby se z libovoln´eho ˇretˇezce mohl st´at atom, vol´an´ı name ho vloˇz´ı mezi jednoduch´e uvozovky. Na vˇsechny v´ yskyty tˇechto uvozovek uvnitˇr ˇretˇezce automaticky pouˇzije tzv. escape sekvenci. To je v tomto pˇr´ıpadˇe proces pˇrid´an´ı zpˇetn´eho lom´ıtka pˇred kaˇzdou uvozovku.
32
Standard ISO Prolog
5.8
Pr´ace se soubory
Pr´ ace se soubory
V Prologu m˚ uˇzeme m´ısto standardn´ıho vstupu a v´ ystupu definovat jednoduˇse konkr´etn´ı soubory. Ty je moˇzn´e otevˇr´ıt pro ˇcten´ı nebo z´apis.
5.8.1
ˇ Cten´ ı
Pro ˇcten´ı ze souboru vyuˇzijeme predik´at see/1. T´ım urˇc´ıme, ˇze vˇsechny vstupn´ı operace budou pracovat pr´avˇe s uveden´ ym souborem. Vytvoˇr´ıme zkuˇsebn´ı soubor test s obsahem: Zkusebni soubor se dvema radky
Abychom si vyzkouˇseli ˇcten´ı, pˇresmˇerujeme vstup na tento soubor a predik´atem seeing/1 ovˇeˇr´ıme, jestli opravdu ze souboru interpret´ator ˇcte [3]: | ?- see(’test’). yes | ?- seeing(’test’). yes
Pro jednoduchou uk´azku, se souborem vytvoˇren´ ym v Linuxu, m˚ uˇzeme pouˇz´ıt jiˇz navrˇzen´ y a vyzkouˇsen´ y predik´at cti radku/1: | ?- cti_radku(X). X = ’Zkusebni soubor’ yes
ˇ ı vstupu funguje jako fronta. Pˇredstavme si, ˇze m´ame otevˇren´ Cten´ y pomysln´ y pr˚ uchod“, kter´ ym pˇrich´azej´ı znaky. Nez´aleˇz´ı na tom, jestli poch´az´ı ” z kl´avesnice nebo ze souboru. Predik´at get0 postupnˇe odebere prvn´ı v ˇradˇe pokaˇzd´e, kdyˇz je zavol´an. Jestliˇze je vstup standardn´ı a ˇz´adn´e znaky nejsou ve frontˇe, ˇcek´a se na vstup z kl´avesnice ze strany uˇzivatele. Vstup ze souboru je ovˇsem koneˇcn´ y a pˇredem urˇcen´ y, takˇze mus´ıme konec souboru hl´ıdat. Jistˇe v´ıte, ˇze konec ˇra´dky v Unixov´em syst´emu je urˇcen´ y netisknuteln´ ym znakem s oznaˇcen´ım LF neboli anglicky Line Feed“. V syst´emu Windows se ” pouˇz´ıvaj´ı dva znaky v poˇrad´ı CR+LF, kde CR znamen´a v angliˇctinˇe Carriage ” Return“.
33
Standard ISO Prolog
Pr´ace se soubory
Tyto znaky poch´az´ı jeˇstˇe z poˇca´tk˚ u komunikace. V tehdejˇs´ıch dob´ach se vypisovalo mechanicky a znak CR mˇel v´ yznam n´avratu tiskov´e hlavy (volnˇe pˇreloˇzeno n´avrat voz´ıku“) na zaˇca´tek ˇr´adky. Znak LF mˇel za n´asledek posun ” pap´ıru o ˇra´dek v´ yˇse. Jednoduˇse tak m˚ uˇzeme pˇridat k p˚ uvodn´ı kontrole konce ˇra´dky v Unixu i kontrolu na konec ˇra´dky v syst´emu Windows. Dalˇs´ı souvisej´ıc´ı kontrola je kontrola na konec souboru. Zde pˇri pokusu o pˇreˇceten´ı dalˇs´ıho znaku dostaneme neexistuj´ıc´ı ASCII k´od −1. Takto nyn´ı vypadaj´ı vˇsechny moˇznosti zpracov´an´ı pˇreˇcten´eho znaku: % Odchyceni odradkovani ve Windows zpracuj_znak(13, Stary_list, Stary_list) :- get0(_). % Odchyceni odradkovani v Linuxu zpracuj_znak(10, Stary_list, Stary_list). % Konec souboru zpracuj_znak(-1, Stary_list, Stary_list) :- !,fail. zpracuj_znak(X, Stary_list, Novy_list) :append(Stary_list, [X], L), cti_znak_rekurzivne(L, Novy_list).
Pokud bychomo chtˇeli podporovat jeˇstˇe nav´ıc platformu Mac OS, vyuˇz´ıvaj´ıc´ı pro odˇra´dkov´an´ı pouze CR, jiˇz by byly kontroly sloˇzitˇejˇs´ı. Kdyˇz z kontroln´ıho pravidla pro Windows odstran´ıme tˇelo, tedy pˇreˇcten´ı dalˇs´ıho znaku (u Windows n´asleduje LF), bude program v Mac OS fungovat spr´avnˇe na u ´kor platformy Windows, kde se bude jeˇstˇe vypisovat pr´avˇe znak LF. S takto definovan´ ymi pravidly m˚ uˇzeme zavolat predik´at cti_radku(X) opakovanˇe: | ?- cti_radku(X). X = ’Zkusebni soubor’ yes | ?- cti_radku(X). X = ’se dvema radky’ yes | ?- cti_radku(X). no
Je vidˇet, ˇze se vol´an´ım predik´atu cti radku rekurzivnˇe pˇreˇcetly vˇsechny znaky aˇz do prvn´ıho v´ yskytu odˇra´dkov´an´ı, kter´e jsme do v´ ypisu nezahrnuli. D´ıky dvˇema kontrol´am funguje z´apis ekvivalentnˇe i se soubory vytvoˇren´ ymi pod syst´emem Windows. Opakovan´e vol´an´ı ˇcte dalˇs´ı znaky od posledn´ıho pˇreˇcten´eho. Na z´akladˇe tˇechto znalost´ı jistˇe zvl´adneme vytvoˇrit predik´at, 34
Standard ISO Prolog
Pr´ace se soubory
kter´ y po ˇra´dc´ıch pˇreˇcte cel´ y soubor. To si uk´aˇzeme d´ale v t´eto kapitole. Kdyˇz nyn´ı pˇrip´ıˇseme do souboru dalˇs´ı ˇr´adky a zkus´ıme znovu pˇreˇc´ıst dalˇs´ı ˇra´dku, nic se uˇz nevyp´ıˇse. Je to d´ano t´ım, ˇze interpret´ator B-Prolog soubor pˇri proveden´ı see naˇcte jednor´azovˇe na vstup a s t´ımto vstupem m˚ uˇzeme d´ale libovolnˇe pracovat. Pokud chceme definovat opˇet standardn´ı vstup, nemus´ıme zn´at jeho pojmenov´an´ı, ale m˚ uˇzeme pouˇz´ıt predik´at seen/0. | ?- seen. yes | ?- seeing(X). X = user yes
Za pomoci predik´atu seeing jsme zjistili, ˇze standardn´ı vstup je pojmenov´an user. Nyn´ı si vytvoˇr´ıme nov´ y soubor pro program, kter´ y bude definovat jednoduch´a pravidla pro pr´aci se soubory. Nazveme si jej Soubory.pro a definujeme si predik´at pro ˇcten´ı. Vyuˇzijeme pˇritom k´od z programu Vstup.pro. Pˇredpokl´adejme, ˇze jsou soubory ve stejn´e sloˇzce, tak abychom ˇca´st nemuseli kop´ırovat, staˇc´ı pouˇz´ıt dobˇre zn´am´ y predik´at consult: ?-consult(’Vstup.pro’). % opakujeme, dokud se dari vypisovat cti_soubor(Soubor) :see(Soubor), repeat, \+ vypis_radku, !, seen. vypis_radku :- cti_radku(X), write(X), nl.
V tˇele predik´atu je pouˇzit jednoduch´ y cyklus. Nejprve zvol´ıme vstupn´ı soubor a pouˇzijeme zar´aˇzku“ v podobˇe predik´atu repeat. Pokaˇzd´e, kdy se ” povede vypsat ˇra´dku, zaj´ıst´ıme negac´ı, aby pravidlo neuspˇelo. T´ım se pˇreˇcte dalˇs´ı ˇra´dka. Aˇz kdyˇz nen´ı co d´al ˇc´ıst, vyhodnocen´ı je pozitivn´ı a dostaneme se k ˇrezu. Ten zde zajiˇst’uje jednoznaˇcnost v´ ysledku. N´asleduje pˇrepnut´ı zpˇet na standardn´ı vstup. Zkusme vypsat na obrazovku upraven´ y textov´ y soubor test: | ?- cti_soubor(test). Zkusebni soubor se dvema radky PS: jeste jeden radek. yes
35
Standard ISO Prolog
5.8.2
Pr´ace se soubory
Z´ apis
Pˇri z´apisu do souboru postupujeme podobnˇe jako pˇri ˇcten´ı. Tak´e staˇc´ı pouze intepret´atoru pˇresmˇerovat standardn´ı v´ ystup na n´aˇs soubor a m˚ uˇzeme pouˇz´ıvat predik´aty jako pˇri klasick´em vypisov´an´ı na obrazovku. Jako u ˇcten´ı jsou pro n´as pro z´apis d˚ uleˇzit´e tˇri predik´aty tell/1, told/0 a telling/1 [3]. Jako ekvivalent k tell/1 je v nˇekter´ ych verz´ıch prologu predik´at append/1, kter´ y umoˇzn´ı z´apis na konec zvolen´eho souboru, zat´ımco tell/1 aktu´aln´ı obsah pˇrep´ıˇse, nebo vytvoˇr´ı soubor nov´ y, pokud neexistuje [3]. Ukaˇzme si pouˇzit´ı v pravidlech: zapis_radku_do_souboru(Soubor, X) :tell(Soubor), write(X), nl, told. zapis_vstup_do_souboru(Soubor) :cti_radku(X), zapis_radku_do_souboru(Soubor, X).
Predik´at zapis radku do souboru/2 pouze pˇresmˇeruje v´ ystup na poˇzadovan´ y soubor, zap´ıˇse text pˇredan´ y promˇennou X a pˇresmˇeruje zpˇet na standardn´ı v´ ystup. Predik´at zapis vstup do souboru/1 rozˇsiˇruje st´avaj´ıc´ı o moˇznost zadat text z kl´avesnice nebo z jin´eho souboru. Podobn´ ym postupem bychom mohli napˇr. upravit obsah vstupn´ıho souboru a po ˇra´dc´ıch jej opˇet uloˇzit do jin´eho souboru. B-Prolog bohuˇzel nepodporuje predik´at append/1, kter´ y je pro pr´aci se soubory velice uˇziteˇcn´ y. Naˇstˇest´ı mimo v´ yˇse jmenovan´e zp˚ usoby existuje jeˇstˇe pˇr´ıstup, kter´ y ocen´ı sp´ıˇse pokroˇcilejˇs´ı program´atoˇri, jelikoˇz je podobn´ y standard˚ um v jin´ ych jazyc´ıch.
5.8.3
Jin´ y zp˚ usob
Pro pokroˇcilejˇs´ı pr´aci s proudem dat smˇeˇruj´ıc´ıch z/do souboru, pouˇzijeme zabudovan´ y predik´at open/3 nebo open/4. Z´apis vypad´a n´asledovnˇe: open(Soubor, Akce, Proud, Moznosti) kde Soubor pˇredstavuje soubor, se kter´ ym chceme pracovat, Akce urˇcuje, co chceme se souborem dˇelat. M´ame moˇznosti read = ˇcten´ı, write = z´apis nebo append = pˇrid´an´ı na konec souboru. Proud je v´ ystupn´ı promˇenn´a pˇredstavuj´ıc´ı pomysln´ y komunikaˇcn´ı kan´al“ se souborem. Moznosti je seznam ” doplˇ nuj´ıc´ıch nastaven´ı. Tento argument m˚ uˇzeme vynechat. Mezi nejzaj´ımavˇejˇs´ı moˇznosti nastaven´ı patˇr´ı pˇrepnut´ı typu ˇcten´ı. Prim´arnˇe se pracuje s obsahem souboru jako s textem a ˇcten´ı ˇci z´apis prob´ıh´a po 36
Standard ISO Prolog
Pr´ace se soubory
znac´ıch. M˚ uˇzeme pˇrepnout typ na bin´arn´ı soubor nastaven´ım type(binary) a pot´e jsou operace bitov´e. Dalˇs´ı moˇznost´ı je pojmenov´an´ı proudu (tzv. alias). K tomu pouˇzijeme libovoln´ y atom, pomoc´ı kter´eho m˚ uˇzeme proud pˇri dalˇs´ı pr´aci identifikovat. Pojmenov´an´ı se urˇcuje predik´atem alias(proud1). Ve standardu jsou definov´any jeˇstˇe moˇznosti nastaven´ı chov´an´ı pˇri dosaˇzen´ı konce souboru pomoc´ı predik´atu eof action/1. Nejzaj´ımavˇejˇs´ı nastaven´ı je eof_action(reset), d´ıky kter´emu se pokus´ı interpret´ator po dosaˇzen´ı konce souboru znovu jej prohledat a zkontrolovat, zda je moˇzn´e ˇc´ıst jeˇstˇe d´ale, v pˇr´ıpadˇe ˇze by do souboru nˇekdo zapsal. Posledn´ı moˇznost´ı je umoˇznˇen´ı posouv´an´ı se v souboru dopˇredu nebo dozadu pomoc´ı predik´atu reposition(true). Posouv´an´ı je ve v´ ychoz´ım nastaˇ ıdic´ı predik´aty je moˇzn´e v seznamu zapsat v libovoln´em ven´ı deaktivov´ano. R´ poˇrad´ı i poˇctu. Abychom mohli pracovat s novˇe otevˇren´ ym proudem, jsou standardem urˇceny speci´aln´ı predik´aty: ˇ ı: Cten´ get_char(Proud, Znak) – ˇcten´ı jednoho znaku get_code(Proud, Kod) – ˇcten´ı numerick´eho k´odu znaku read_term(Proud, Term, Moznosti) a read(Proud, Term) – ˇcten´ı jednoho termu Z´apis: put_char(Proud, Znak) – z´apis jednoho znaku put_code(Proud, Kod) – z´apis numerick´eho k´odu znaku write_term(Proud, Term, Moznosti) a write(Proud, Term) – z´apis jednoho termu ym nastaPokud pouˇzijeme predik´at set output/1 nebo set input/1, kter´ v´ıme aktu´aln´ı proud ˇcten´ı/z´apisu na n´aˇs novˇe otevˇren´ y, m˚ uˇzeme pouˇz´ıvat i predik´aty read/1, write/1 a podobn´e, kter´e nepotˇrebuj´ı jako argument tento proud [3]. Pro uk´azku si m˚ uˇzeme zkusit jednoduch´ y z´apis na konec naˇseho testovac´ıho souboru: | ?- open(test, append, Proud), set_output(Proud), write(’ahoj’), close(Proud). Proud = (stream)[10002] yes
37
Standard ISO Prolog
Moduly
Mus´ıme si d´avat pozor tak´e na d˚ usledn´e uzav´ır´an´ı otevˇren´ ych proud˚ u pomoc´ı predik´atu close/1. Pokud uzavˇreme proud, na kter´ y jsme pˇresmˇerovali vstup nebo v´ ystup, zruˇs´ı se i pˇresmˇerov´an´ı a je pouˇzit standardn´ı proud. Aktu´alnˇe nastaven´ y proud zjist´ıme vol´an´ım predik´atu current input/1 nebo current output/1. V´ yˇse zm´ınˇen´e predik´aty tell, see apod. pouˇz´ıvaj´ı vnitˇrnˇe pr´avˇe tyto konstrukce, jen jsou zapouzˇren´e do pˇr´ıvˇetivˇejˇs´ı podoby, na u ´kor moˇznosti jejich u ´prav.
5.9
Moduly
Pozornost si zaslouˇz´ı i moduly, pˇrestoˇze interpret´ator B-Prolog je v aktu´aln´ı verzi nepodporuje. Modul je ekvivalent tˇr´ıdy v jazyce Java. Do modulu m˚ uˇzeme zapouzdˇrit predik´aty, kter´e spolu u ´zce souvis´ı a zpˇr´ıstupn´ıme veˇrejnˇe pouze ty, kter´e chceme. Ukaˇzme si definici takov´eho modulu, bohuˇzel bez moˇznosti praktick´eho vyzkouˇsen´ı: ?- module(informace). ?- export([vypis_info/0]). ?- begin_module(informace). vypis_info :- write(’Nasleduji informace: ’), info(X), write(X), !. info(’Verejna informace’). info(’Tajna informace’). ?- end_module.
Pˇri definici z´aleˇz´ı na poˇrad´ı predik´at˚ u. Prvn´ı mus´ıme definovat nov´ y modul direktivou module/1 a n´asleduje seznam veˇrejnˇe pˇr´ıstupn´ ych predik´at˚ u, kter´ y pˇred´ame jako argument pˇri vol´an´ı export/1. Mezi direktivami begin module/1 a end module/0 definujeme libovoln´ y program, kde se mus´ı vyskytovat predik´aty vypsan´e v direktivˇe export. Pokud chceme nyn´ı v jin´em programu volat predik´aty definovan´e v urˇcit´em modulu, mus´ı b´ yt modul v interpret´atoru naˇcten´ y a v poˇzadovan´em programu jej importujeme direktivou import/1. Direktivy jsou vˇzdy platn´e pouze v r´amci jednoho souboru. Kdyˇz budeme cht´ıt zavolat neveˇrejn´ y predik´at z nˇejak´eho modulu, je to tak´e moˇzn´e za pomoci z´apisu nazev_modulu:vnitrni_predikat. Pomoc´ı z´apisu predikat @ jiny_modul ˇrekneme interpret´atoru, ˇze chceme zavolat 38
´ Uprava nahran´e datab´aze
Standard ISO Prolog
predik´at predikat z modulu jiny_modul i v pˇr´ıpadˇe, ˇze bude predikat definov´an v aktu´aln´ım programu [3]. Pˇredpokl´adejme naˇcten´ı programu, kde je importov´an modul informace definovan´ y v´ yˇse: | ?- vypis_info. Nasleduji informace: Verejna informace yes | ?- informace:info(X). X = ’Verejna informace’ ?; X = ’Tajna informace’ yes
Pˇri vol´an´ı predik´atu info/1 bez definov´an´ı modulu by interpret´ator skonˇcil s v´ yjimkou.
5.10
´ Uprava nahran´ e datab´ aze
V intepret´atoru mˇen´ıme obsah datab´aze hojnˇe napˇr. v pr˚ ubˇehu testov´an´ı nov´eho programu za pouˇzit´ı predik´atu consult/1 ˇci reconsult/1. Kdyˇz po nˇekolika takto nahran´ ych programech vyp´ıˇseme obsah datab´aze vol´an´ım predik´atu listing/0, zjist´ıme, ˇze velk´a spousta nepotˇrebn´ ych predik´at˚ u z˚ ust´av´a v datab´azi. Predik´aty i pravidla zde z˚ ust´avaj´ı do vypnut´ı interpret´atoru. Po spuˇstˇen´ı interpret´atoru m˚ uˇzeme do pr´azdn´e datab´aze pˇridat vlastn´ı predik´aty za pomoci dobˇre zn´am´eho vol´an´ı consult/1. Pokud ale nepˇredpokl´ad´ame velk´ y rozsah programu, staˇc´ı pouˇz´ıt jeden z predik´at˚ u assert/1, asserta/1 nebo assertz/1. Jejich argumentem je libovoln´ y predik´at nebo atom. Takto m˚ uˇzeme pˇrid´avat predik´aty i k jiˇz nahran´emu programu. Predik´aty assert a assertz pˇrid´avaj´ı fakta na konec, predik´at asserta pˇrid´av´a na zaˇc´atek. Jestliˇze chceme pˇridat v´ıce predik´at˚ u najednou nebo zad´avat pravidla, pouˇzijeme vol´an´ı consult(user). To m´ısto ze souboru naˇc´ıt´a vstup z kl´avesnice a m˚ uˇzeme ps´at stejn´ ym zp˚ usobem, jako zapisujeme do souboru. V B-Prologu ukonˇc´ıme zad´av´an´ı kombinac´ı kl´aves ctrl + D. V pˇr´ıpadˇe, ˇze chceme fakta z datab´aze odstranit, pouˇzijeme predik´at retract/1. Jako argument zap´ıˇseme predik´at a prvn´ı v datab´azi, se kter´ ym se tento predik´at unifikuje, bude z datab´aze vymaz´an. Pro vymaz´an´ı vˇsech odpov´ıdaj´ıc´ıch pouˇzijeme retractall/1. Pro vymaz´an´ı vˇsech definic konkr´etn´ıho predik´atu pouˇzijeme abolish(Funktor/Arita) [11]. Pozor na fakta a pravidla nahran´a pomoc´ı predik´atu consult. Na jejich 39
´ Uprava nahran´e datab´aze
Standard ISO Prolog
smaz´an´ı nem´ame dostateˇcn´a opr´avnˇen´ı. Vyzkouˇs´ıme si budov´an´ı datab´aze v praxi: | ?- assert(pred(a)), assert(pred(b)), assert(pred(c) yes | ?- asserta(pred(d)). yes | ?- assert(pred(1, a)). yes | ?- listing. pred(d). pred(a). pred(b). pred(c). pred(1, a). yes | ?- retract(pred(X)). X = d ?; X = a ? yes | ?- listing. pred(b). pred(c). pred(1, a). yes | ?- abolish(pred/1). yes | ?- listing. pred(1, a). | ?- abolish. yes | ?- listing. yes
Pˇredoposledn´ı vol´an´ı predik´atu abolish/0 maˇze vˇsechny n´ami definovan´a fakta.
40
6 V´yvojov´e prostˇred´ı B-Prolog V z´avˇeru se pod´ıv´ame podrobnˇeji na interpret´ator B-Prolog. Hlavn´ım c´ılem je uk´azat moˇznosti Prologu nejen jako interpretovan´eho jazyka, ale tak´e jako n´astroje pro ˇreˇsen´ı d´ılˇc´ıch probl´em˚ u ve formˇe samostatn´eho spustiteln´eho programu nebo jako souˇca´st programu napsan´eho v jin´em jazyce. Jak se prostˇred´ı instaluje a spouˇst´ı jsme si jiˇz ˇrekli v kapitole 2.3.2. V´ıme, ˇze interpret´ator vol´ı v´ ychoz´ı sloˇzku takovou, ze kter´e byl z pˇr´ıkazov´e ˇra´dky pˇr´ıkazem spuˇstˇen. Pˇri spuˇstˇen´ı m˚ uˇzeme pouˇz´ıvat parametry. Nejzaj´ımavˇejˇs´ı je parametr -g "prikazy", kter´ y vykon´a zadan´e pˇr´ıkazy (c´ıle) bezprostˇrednˇe po startu interpret´atoru. Jestliˇze chceme prov´est pˇr´ıkaz jeˇstˇe pˇred startem, nap´ıˇseme na konec sledu pˇr´ıkaz˚ u predik´at $bp_top_level, kter´ y inicializuje start interpret´atoru aˇz po tˇechto u ´konech.
6.1
Zmˇ eny oproti standardu
B-Prolog z velk´e ˇc´asti podporuje standard ISO Prolog. Nˇekdy ale nejsou funkce totoˇzn´e s navrhovan´ ymi poˇzadavky standardu. Obsahuje tak´e velk´e mnoˇzstv´ı pˇridan´ ych predik´at˚ u. Vˇsechny moˇznosti B-Prologu jsou seps´any v rozs´ahl´e pˇr´ıruˇcce pˇrikl´adan´e vˇzdy k aktu´aln´ı verzi interpret´atoru [11]. Uk´aˇzeme si moˇznosti, kter´e by n´am pˇri tvorbˇe pˇredchoz´ıch pˇr´ıklad˚ u usnadnily pr´aci nebo pomohly v jejich rozˇs´ıˇren´ı.
6.1.1
Vlastn´ı programy
Pˇri pr´aci ocen´ıme predik´at []/1, kter´ y m´a stejnou funkci jako jiˇz dobˇre zn´am´ y predik´at consult/1. Cesta k souboru se pouze zap´ıˇse do hranat´ ych z´avorek stejnˇe jako seznam. M˚ uˇzeme tak´e vypsat najednou v´ıce soubor˚ u, kter´e se maj´ı nahr´at. Fakta a pravidla definovan´a v tˇechto souborech se nahraj´ı do datab´aze a vid´ıme je pˇri pouˇzit´ı listing/0. Interpret´ator umoˇzn ˇuje kompilaci do bajtk´odu podobnˇe jako v jazyce Java. Z naˇsich program˚ u m˚ uˇzeme takto vytvoˇrit samostatnˇe funkˇcn´ı celky. Ke kompilaci slouˇz´ı predik´at compile/1, kter´ y jako argument pˇrij´ım´a n´azev souboru a na jeho z´akladˇe vytvoˇr´ı bin´arn´ı soubor se stejn´ ym n´azvem a pˇr´ıponou .out. 41
V´yvojov´e prostˇred´ı B-Prolog
Zmˇeny oproti standardu
Zkompilovan´ y program naˇcteme bud’ pomoc´ı load/1 nebo zad´an´ım n´azvu jako argumentu pˇri spouˇstˇen´ı interpret´atoru. Pro rychlou kompilaci a spuˇstˇen´ı vyuˇzijeme predik´at cl/1, kter´ y ale nevytvoˇr´ı bin´arn´ı soubor. Standardnˇe v samostatn´ ych programech definujeme vstupn´ı predik´at main, kter´ y by se mˇel automaticky po naˇcten´ı programu vyhodnotit. Pˇri zad´an´ı argumentem se po dokonˇcen´ı bˇehu programu ukonˇc´ı i interpret´ator. Za n´azev programu m˚ uˇzeme vypsat libovoln´e argumenty, kter´e v programu naˇcteme pouˇzit´ım get_main_args(Argumenty) do promˇenn´e Argumenty jako seznam. Spuˇstˇen´ı funguje ve starˇs´ıch verz´ıch, bohuˇzel v aktu´aln´ı verzi 8.1 se main nezavol´a, ale pouze se spust´ı interpret´ator s nahran´ ym programem.
6.1.2
Komunikace s OS
Interpret´ator m´a v sobˇe nav´ıc predik´aty pro komunikaci s operaˇcn´ım syst´emem. Kromˇe predik´atu write, kter´ y umoˇzn ˇuje v´ ypis na obrazovku je zde ˇrada dalˇs´ıch. Libovoln´ y pˇr´ıkaz syst´emu zad´ame prostˇrednictv´ım predik´atu system(Prikaz, Status), kde Status je voliteln´a promˇenn´a, kam se uloˇz´ı n´avratov´a hodnota po vyhodnocen´ı pˇr´ıkazu Prikaz. Pro zjiˇstˇen´ı aktu´aln´ı cesty, se kterou interpret´ator pracuje, pouˇzijeme getcwd(Cesta). Pro zmˇenu cesty spouˇz´ı predik´at cd/1, kter´ y jako argument pˇrij´ım´a cestu absolutn´ı nebo relativn´ı v naˇsem souborov´em syst´emu. Seznam soubor˚ u ve sloˇzce z´ısk´ame vyhodnocen´ım directory_files(Slozka, Seznam). Lze tak´e kop´ırovat soubory pomoc´ı copy file/2, pˇr´ıpadnˇe mazat soubory vol´an´ım delete file/1. Sloˇzky maˇzeme vol´an´ım delete directory/1 a vytv´aˇr´ıme pomoc´ı make directory/1. Aktu´aln´ı datum vyp´ıˇseme predik´atem date/1 a aktu´aln´ı syst´emov´ y ˇcas vol´an´ım time(Hodiny, Minuty, Sekundy).
6.1.3
Vol´ an´ı predik´ at˚ u
Predik´aty vol´ame ve vˇetˇsinˇe interpret´ator˚ u stejn´ ym zp˚ usobem. Pro vyhodnocen´ı c´ıle existuje ve standardu jeˇstˇe predik´at call(Cil), kter´ y funguje totoˇznˇe jako klasick´e zad´an´ı c´ıle do interaktivn´ıho rozhran´ı, a d´ale predik´at once(Cil), kter´ y zabraˇ nuje backtrackingu stejnˇe jako vol´an´ı call(Cil, !). Uˇziteˇcn´e rozˇs´ıˇren´ı pˇredstavuje predik´at time out/3, kter´ y se zapisuje ve tvaru time_out(Cil, Cas, Vysledek). Ten jednoduˇse vyhodnot´ı Cil po42
V´yvojov´e prostˇred´ı B-Prolog
Zmˇeny oproti standardu
moc´ı vol´an´ı once, ale pokud pˇred vyhodnocen´ım uplyne ˇcas nastaven´ y argumentem Cas, vol´an´ı se ukonˇc´ı a v promˇenn´e Vysledek je pˇriˇrazen atom time_out. V opaˇcn´em pˇr´ıpadˇe se Vysledek unifikuje na success. M˚ uˇzeme se setkat s pˇr´ıpadem, kdy potˇrebujeme vyvolat akci aˇz kdyˇz se pˇriˇrad´ı promˇenn´e hodnota. K tomu vyuˇzijeme predik´at freeze/2. Prvn´ı argument je promˇenn´a a druh´ y je c´ıl, kter´ y se m´a zavolat. Ten je zavol´an pomoc´ı once pr´avˇe tehdy, kdyˇz je promˇenn´a unifikov´ana s konkr´etn´ım termem. Predik´at forall(Moznosti, Volani) nalezne postupnˇe vˇsechny moˇznosti vyhodnocen´ı termu Moznosti a pro kaˇzdou z moˇznost´ı vyvol´a c´ıl Volani. Jako pˇr´ıklad je v manu´alu uvedeno proch´azen´ı prvk˚ u seznamu. Zaj´ımav´e je pro n´as i tˇelo definice tohoto predik´atu: forall(Moznosti, Volani) :- \+ (call(Moznosti), \+ call(Volani)). | ?- forall(member(X,[a,b,c]),write(X)). abc yes
6.1.4
Pr´ ace se seznamy
B-Prolog definuje mimo jin´e i z´akladn´ı operace se seznamy, kter´e se ve standardu nevyskytuj´ı. V posledn´ım pˇr´ıkladu vid´ıme pouˇzit´ı nov´eho predik´atu member(X, Seznam), kter´ y kontroluje, zda X je prvkem seznamu Seznam. Pokud promˇennou X nepˇriˇrad´ıme, bude unifikov´ana s prvn´ım prvkem seznamu. Pomoc´ı backtrackingu m˚ uˇzeme postupnˇe prohledat cel´ y seznam od zaˇca´tku do konce. Pouˇz´ıvan´ y je tak´e predik´at length/2, kter´ y zjist´ı d´elku seznamu uveden´eho v prvn´ım argumentu a ˇc´ıslo unifikuje s druh´ ym argumentem. Pomoc´ı sort/2 seˇrad´ıme seznam v prvn´ım argumentu vzestupnˇe. U predik´atu sort/3 m˚ uˇzeme nav´ıc jako prvn´ı argument definovat ˇrad´ıc´ı oper´ator <, >, =< nebo >=. Predik´at keysort(S1, S2) pracuje se seznamem p´ar˚ u a jeho v´ ystupem je seznam seˇrazen´ y podle prvn´ıho prvku z p´aru: | ?- keysort([(3,a), (2,c), (1,b)], Serazene). Serazene = [(1,b),(2,c),(3,a)] Yes
Je to obdoba mapy v jazyce Java, kde prvn´ı prvek p´aru je kl´ıˇc, druh´ y prvek je hodnota.
43
V´yvojov´e prostˇred´ı B-Prolog
Zmˇeny oproti standardu
Ze seznamu m˚ uˇzeme konkr´etn´ı prvky u ´plnˇe vymazat, nebo je postupnˇe odeb´ırat n´asleduj´ıc´ım zp˚ usobem: | ?List yes | ?List List List no
delete([a,b,a,b,c,a,g], a, List). = [b,b,c,g] select(a, [a,b,a,b,c,a,g], List). = [b,a,b,c,a,g] ?; = [a,b,b,c,a,g] ?; = [a,b,a,b,c,g] ?;
Prvek ze seznamu na konkr´etn´ı pozici vybereme pomoc´ı predik´atu nth/2 (prvky ˇc´ıslovan´e od jedniˇcky) nebo nth0/2 (prvky jsou ˇc´ıslovan´e od nuly): | ?- nth(2, [1,2,3], X). X = 2 yes | ?- nth0(2, [1,2,3], X). X = 3 yes
6.1.5
Cyklus, kolekce a form´ atov´ an´ı v´ ystupu
Cykus foreach se zapisuje se ve tvaru: foreach(X1 in K1, ..., Xn in Kn, Lokalni_promenne, Cil) Xi pˇredstavuje promˇennou pˇr´ıpadnˇe term, Ki je kolekce, pˇres kterou se bude iterovat. Iterac´ı m˚ uˇze b´ yt libovoln´e mnoˇzstv´ı a projdou se vˇsechny moˇznosti stejn´ ym postupem, jak funguje backtracking, tedy zleva doprava a s vyvolanou opakovanou zmˇenou moˇzn´ ych hodnot. Promˇenn´a Lokalni pˇredstavuje seznam promˇenn´ ych, kter´e jsou doˇcasn´e pro jednu iteraci a Cil je libovoln´ y c´ıl, kter´ y se bude pˇri kaˇzd´e iteraci vyhodnocovat. Abychom proˇsli vˇsechny prvky seznamu, m˚ uˇzeme pouˇz´ıt cyklus foreach n´asledovnˇe: | ?- foreach(N in [1,2,3], (write(’Cislo ’), writeln(N))). Cislo 1 Cislo 2 Cislo 3 yes
44
V´yvojov´e prostˇred´ı B-Prolog
Zmˇeny oproti standardu
Pod´ıvejme se na sloˇzitˇejˇs´ı z´apis vˇcetnˇe pouˇzit´ı kolekce ˇc´ısel a form´atov´an´ı v´ ystupu: | ?- foreach(I in 1..3,J in 2..-1..1,format("{~d, ~d}.\n",[I,J])). {1, 2}. {1, 1}. {2, 2}. {2, 1}. {3, 2}. {3, 1}. yes
Kolekce ˇc´ısel ve tvaru Od..Do nebo Od..Krok..Do se pouˇz´ıv´a pouze v cyklu. Od je poˇca´teˇcn´ı ˇc´ıslo, Do je koneˇcn´e ˇc´ıslo a Krok se k p˚ uvodn´ımu ˇc´ıslu v kaˇzd´e iteraci pˇriˇc´ıt´a. V´ ychoz´ı hodnota kroku je 1. Z jazyka C zn´ame funkci printf, kter´e zad´ame prvn´ım argumentem ˇretˇezec se speci´aln´ımi form´atovac´ımi znaˇckami a druh´ y argument je v´ yˇcet prvk˚ u, kter´e se do ˇretˇezce za form´atovac´ı znaˇcky dosad´ı. Ekvivalentnˇe funguje predik´at format/2. Znaˇcky v ˇretˇezci zaˇc´ınaj´ı znakem ’~’. V naˇsem pˇr´ıkladu ~d znaˇc´ı ˇc´ıslo. M˚ uˇzeme tak´e zapsat ~Nd, kde N je ˇc´ıslo, kter´e urˇcuje, jak velk´e m´ısto m´a b´ yt pro ˇc´ıslo vyhrazeno. Pokud se nevyuˇzije cel´e m´ısto, ˇc´ıslo se dopln´ı zleva mezerami. Dalˇs´ı moˇznost´ı je ~a pro v´ ypis atomu bez uvozovek, ~Nc pro v´ ypis N stejn´ ych znak˚ u (pokud N neuvedeme, vyp´ıˇse se jeden). Pro vyps´an´ı znaku ’~’ pouˇzijeme z´apis ~~. Druh´ y argument bude obsahovat jeden nebo seznam vˇsech prvk˚ u pouˇzit´ ych ve form´atovac´ım ˇretˇezci. Form´atovac´ı znaˇcky jsou vyps´any v n´avodu [11] na stranˇe 45.
6.1.6
M´ od ladˇ en´ı
Pˇri tvorbˇe program˚ u vyuˇzijeme m´od ladˇen´ı (anglicky debugger), kter´ y n´am m˚ uˇze pomoci s odhalen´ım chyb v n´avrhu pravidel a predik´at˚ u. M´od v interpret´atoru spust´ıme predik´atem trace/0. V tomto m´odu vid´ıme vyhodnocen´ı jednotliv´ ych c´ıl˚ u od zaˇca´tku do konce, vˇcetnˇe veˇsker´e unifikace. Ladic´ı m´od opust´ıme vol´an´ım notrace/0:
45
V´yvojov´e prostˇred´ı B-Prolog
Zmˇeny oproti standardu
| ?- trace yes {Trace mode} | ?- write(a). Call: (1) write(a) ? a Exit: (1) write(a) ? yes
Vyhodnocov´an´ı se hned zastav´ı a pomoc´ı kl´avesy enter se m˚ uˇzeme posouvat po kroc´ıch, kter´e interpret´ator ˇcin´ı. P´ısmeno ’s’ pˇreskakuje aktu´alnˇe volan´ y predik´at a zastav´ı se aˇz po jeho u ´spˇeˇsn´em ˇci ne´ uspˇeˇsn´em vyhodnocen´ı. P´ısmenem ’r’ nech´ame lad´ıc´ı m´od vypsat vˇsechny kroky aˇz do konce vyhodnocen´ı. V zastaven´em ladˇen´ı se d´a vyvolat n´apovˇeda pomoc´ı p´ısmene ’h’ nebo znakem ’?’. V programu m˚ uˇzeme definovat pouze konkr´etn´ı body, na kter´ ych se chceme zastavit a prozkoumat je pˇri vyhodnocov´an´ı. K tomu pouˇzijeme predik´at spy(Funktor/Arita). Lad´ıc´ı v´ ypisy jsou pot´e od vol´an´ı urˇcen´eho predik´atu stejn´e, jako u trace. Jeden bod zastaven´ı odebereme vol´an´ım nospy/1 a vˇsechny body vol´an´ım nospy/0.
6.1.7
Glob´ aln´ı promˇ enn´ e
V programech m˚ uˇzeme definovat promˇenn´e, kter´e jsou pot´e pouˇziteln´e napˇr´ıˇc cel´ ym bˇehem programu. Glob´aln´ı promˇennou definujeme predik´atem global_set(Nazev, Hodnota). Hodnotu glob´aln´ı promˇenn´e z´ısk´ame vol´an´ım global_get(Nazev, Hodnota). N´azev mus´ı b´ yt atom, hodnota je libovoln´ y term. Zda je zadan´ y atom glob´aln´ı promˇenn´a zjist´ıme pomoc´ı is global/1: | ?- global_set(promenna, 3). yes | ?- is_global(promenna). yes | ?- global_get(promenna, Hodnota). Hodnota = 3 yes
46
V´yvojov´e prostˇred´ı B-Prolog
6.2
Propojen´ı s imperativn´ımi jazyky
Propojen´ı s imperativn´ımi jazyky
Kl´ıˇcovou vlastnost´ı dneˇsn´ıch interpret´ator˚ u je moˇznost oboustrann´e spolupr´ace Prologovsk´eho programu s klasick´ ym pˇr´ıstupem k programov´an´ı. Nejˇcastˇeji se setk´ame s propojen´ım s jazyky Java, C, C++ nebo C#. To se odv´ıj´ı od toho, v jak´em jazyce byl interpret´ator naps´an. Abychom mohli realizovat napˇr. propojen´ı s jazykem C, potˇrebujeme speci´aln´ı knihovny nebo zdrojov´e k´ody interpret´atoru. V nˇekter´ ych implementac´ıch jsou volnˇe dostupn´e, ale B-Prolog se bohuˇzel ˇrad´ı mezi ty, kter´e maj´ı tyto funkce draze zaplacen´e. Pod´ıv´ame se pouze teoreticky na propojen´ı s jazykem C. Podpora jazyka Java nen´ı v aktu´aln´ı verzi interpret´atoru pˇr´ıliˇs rozˇs´ıˇrena. V n´avodu je podrobnˇe pops´ano, jak´ ym zp˚ usobem m˚ uˇzeme vyvol´avat programov´e segmenty jazyka C v Prologu a obr´acenˇe, vˇcetnˇe praktick´ ych pˇr´ıklad˚ u. Uk´aˇzeme si struˇcnˇe, jak s propojen´ım zaˇc´ıt. Nejprve je tˇreba pˇridat do syst´emu konstantu BPDIR, kter´a bude odkazovat na instalaci B-Prologu. Zdrojov´e k´ody bychom mˇeli po zakoupen´ı licence naj´ıt ve sloˇzce Emulator.
6.3 6.3.1
Propojen´ı s jazykem C Vol´ an´ı C z Prologu
Pro vytvoˇren´ı funkc´ı v jazyce C takov´ ych, abychom je mohli volat z interpret´atoru jako predik´aty, je potˇreba m´ıt k dispozici veˇsker´e zdrojov´e k´ody interpret´atoru. Vytv´aˇren´ı takov´eho predik´atu prob´ıh´a ve tˇrech kroc´ıch: 1) Vytvoˇr´ıme soubor, kam definujeme funkci, kter´a bude prov´adˇet poˇzadovanou akci. Funkce mus´ı b´ yt bez argument˚ u a mus´ı vracet celoˇc´ıselnou hodnotu. Ke komunikaci s interpret´atorem vyuˇz´ıv´ame sadu pˇredpˇripraven´ ych funkc´ı. Ty jsou deklarovan´e v hlaviˇckov´em souboru bprolog.h, kter´ y mus´ıme zahrnout do naˇseho programu pomoc´ı #include "bprolog.h". Pouˇzit´ı tˇechto funkc´ı si pˇribl´ıˇz´ıme d´ale. 2) Novˇe vytvoˇren´ y soubor s funkc´ı mus´ıme zahrnout do k´odu interpret´atoru, nejˇcastˇeji pˇr´ımo do cpreds.c, kde jsou definov´any zabudovan´e preˇ dik´aty zapsan´e v jazyce C. Reknˇ eme, ˇze naˇse funkce je deklarov´ana jako int moje_funkce() a nov´ y predik´at urˇc´ıme jako volaniC/2.
47
V´yvojov´e prostˇred´ı B-Prolog
Propojen´ı s jazykem C
Do tˇela funkce Cboot() pˇrid´ame n´asleduj´ıc´ı ˇra´dky: extern int moje_funkce(); insert_cpred("volaniC", 2, moje_funkce);
3) Pˇrekompilujeme cel´ y interpret´ator a po spuˇstˇen´ı m˚ uˇzeme volat predik´at volaniC/2 stejnˇe, jako ostatn´ı zabudovan´e predik´aty. Vrat’me se k vytv´aˇren´ı samotn´e funkce. Kaˇzd´ y term je pops´an strukturou TERM, se kterou pracuj´ı pˇreddefinovan´e funkce. Strukturu z´ısk´ame jako n´avratovou hodnotu funkce nebo ji pˇred´ame argumentem. V aktu´aln´ı verzi intepret´atoru zaˇc´ınaj´ı n´azvy vˇsech funkc´ı pˇredponou bp “. ” I pˇresto, ˇze nov´ y predik´at bude bin´arn´ı, funkci deklarujeme bez argument˚ u. Ty n´aslednˇe z´ısk´ame vol´an´ım funkce: TERM bp_get_call_arg(int i, int arita) kde i je i-t´y argument (poˇc´ıt´ano od 1) a arita je arita predik´atu. Existuje sada funkc´ı, kter´e kontroluj´ı typ termu. N´avratov´e hodnoty jsou BP_TRUE pˇri u ´spˇechu nebo BP_FALSE pˇri ne´ uspˇechu. N´asleduje v´ yˇcet nˇekolika funkc´ı a podm´ınky jejich u ´spˇeˇsn´eho vyhodnocen´ı: int bp_is_integer(TERM t) – t je cel´e ˇc´ıslo int bp_is_atom(TERM t) – t je atom int bp_is_list(TERM t) – t je seznam a dalˇs´ı. Kontroly potˇrebujeme pro konverzi struktury TERM na datov´ y typ, kter´ y m˚ uˇzeme snadno zpracovat pomoc´ı klasick´ ych postup˚ u v C. Pokud zn´ame typ termu, m˚ uˇzeme pouˇz´ıvat odpov´ıdaj´ıc´ı funkce: int bp_get_integer(TERM t) – vr´at´ı celoˇc´ıselnou hodnotu termu t double bp_get_float(TERM t) – vr´at´ı re´alnou hodnotu termu t (char *) bp_get_name(TERM t) – vr´at´ı ukazatel na ˇretˇezec, kter´ y odpov´ıd´a n´azvu atomu nebo struktury t int bp_get_arity(TERM t) – vr´at´ı aritu atomu nebo struktury t Pokud se konverze nepovede, jsou vr´aceny v´ ychoz´ı (nulov´e) hodnoty a globaln´ı promˇenn´a exception je nastavena na chybovou hodnotu. Jazyk C nepodporuje vyvol´av´an´ı a odchyt´av´an´ı v´ yjimek, promˇennou proto mus´ıme kontrolovat sami. Dva termy m˚ uˇzeme mezi sebou unifikovat pomoc´ı funkce: int bp_unify(TERM t1, TERM t2) a jako n´avratovou hodnotu dostaneme BP_TRUE nebo BP_FALSE. Argument na i-t´e pozici z´ısk´ame vol´an´ım funkce: 48
V´yvojov´e prostˇred´ı B-Prolog
Propojen´ı s jazykem C
TERM bp_get_arg(int i, TERM t) jej´ıˇz n´avratovou hodnotou je term na poˇzadovan´e pozici, pokud existuje. V opaˇcn´em pˇr´ıpadˇe je ozn´amena chyba. Pr´ace se seznamy je realizov´ana funkcemi: TERM bp_get_car(TERM t) a TERM bp_get_cdr(TERM t) kde car je prvn´ı prvek seznamu a cdr je zbytek, tedy opˇet seznam. V´ ypis termu zajist´ı funkce void bp_write(TERM t). Posledn´ı moˇznost´ı je vytv´aˇren´ı vlastn´ıch term˚ u v k´odu jazyka C: TERM bp_build_var() – vytvoˇr´ı pr´azdnou promˇennou TERM bp_build_atom(char *nazev) – vytvoˇr´ı atom TERM bp_build_integer(int cislo) – vytvoˇr´ı celoˇc´ıseln´ y term TERM bp_build_nil() – vytvoˇr´ı pr´azdn´ y seznam TERM bp_build_list() – vytvoˇr´ı pr´azdn´ y seznam, kter´ y m˚ uˇzeme libovolnˇe definovat TERM bp_build_structure(char *nazev, int arita) – vytvoˇr´ı strukturu nazev/arita, jej´ıˇz argumenty m˚ uˇzeme libovolnˇe definovat Jednoduch´ y pˇr´ıklad k t´eto problematice nalezneme v manu´alu [11] na stranˇe 99.
6.3.2
Vol´ an´ı Prologu z C
Pokud chceme z programu napsan´eho v jazyce C zavolat sekvenci jazyka Prolog, zmˇen´ıme zdrojov´ y k´od spouˇstˇec´ıho souboru main.c za n´aˇs vlastn´ı. Pred vol´an´ım predik´at˚ u mus´ıme pouˇz´ıt funkci: initialize_bprolog(int argc, char *argv[]) Ta alokuje vˇsechnu potˇrebnou pamˇet’ a nahraje sadu zabudovan´ ych predik´at˚ u. Pokud se nepodaˇr´ı interpret´ator spustit, funkce vr´at´ı hodnotu BP_ERROR. Jednotliv´e c´ıle m˚ uˇzeme volat pomoc´ı int bp_call_string(char *cil) zad´an´ım termu ve formˇe ˇretˇezce nebo pomoc´ı struktury TERM vol´an´ım funkce int bp_call_term(TERM cil). Ta nav´ıc podporuje unifikaci promˇenn´ ych. Dalˇs´ı moˇznost´ı je pouˇzit´ı funkce bp mount query string pˇrij´ımaj´ıc´ı ˇretˇezec nebo bp mount query term pˇrij´ımaj´ıc´ı strukturu TERM. Tyto funkce napl´anuj´ı“ dalˇs´ı c´ıl k vyhodnocen´ı. Pot´e m˚ uˇzeme v´ıcekr´at za sebou pou” ˇz´ıt funkci int bp_next_solution(), kter´a nalezne dalˇs´ı ˇreˇsen´ı. Pokud nen´ı ˇz´adn´ y c´ıl napl´anov´an, jej´ı n´avratov´a hodnota bude BP_ERROR, pokud jiˇz dalˇs´ı c´ıl nelze vyhodnotit, vr´at´ı BP_FALSE.
49
7 Z´avˇer Jiˇz od zaˇca´tku pr´ace je br´an zˇretel na ˇcten´aˇre, kteˇr´ı s Prologem tepreve zaˇc´ınaj´ı. Pˇrestoˇze pracuji v´ yhradnˇe s prostˇred´ım B-Prolog, ˇcten´aˇr m´a ˇsanci nauˇcit se z´aklady jazyka Prolog i za pouˇzit´ı jin´eho interpret´atoru. M˚ uˇze si vybrat na z´akladˇe struˇcn´eho shrnut´ı aktu´aln´ıch implementac´ı, pˇr´ıpadnˇe jak´ ykoliv jin´ y interpret´ator podporuj´ıc´ı standard ISO Prolog. Z´aleˇz´ı na individu´aln´ıch poˇzadavc´ıch na n´asledn´e vyuˇzit´ı jazyka. Praktick´e pˇr´ıklady se prol´ınaj´ı s teori´ı. Samozˇrejmost´ı je pozvoln´e stupˇ nov´an´ı n´aroˇcnosti prezentovan´ ych pˇr´ıklad˚ u. Ty jsou nejdˇr´ıve naps´any jednoduch´ ym zp˚ usobem a d´ale rozˇsiˇrov´any. C´ılem je vytv´aˇret programy dostateˇcnˇe dekomponovan´e s n´aslednou moˇznost´ı snadn´eho rozˇs´ıˇren´ı funkcionality. Na uk´azk´ach poukazuji na z´apisy, kter´ ym je dobr´e se vyvarovat a navrhuji, ˇ aˇr by mˇel b´ jak d´ale a l´epe pˇri tvorbˇe program˚ u postupovat. Cten´ yt n´aslednˇe schopen ps´at programy ˇcistˇe, a mˇel by m´ıt dostateˇcn´e znalosti pro samostatn´e ˇreˇsen´ı probl´em˚ u spojen´ ych s n´avrhem sloˇzitˇejˇs´ıch program˚ u v Prologu. Pro u ´ˇcely v´ yuky pˇredmˇetu KIV/UZI byla, na z´akladˇe podklad˚ u od vedouc´ıho pr´ace, vytvoˇrena pˇrehledn´a prezentace shrnuj´ıc´ı d˚ uleˇzit´e faktory z´akladn´ı pr´ace s Prologem. Ta bude n´aslednˇe um´ıstˇena na webov´e str´anky pˇredmˇetu spoleˇcnˇe s programy, kter´e jsou k n´ahledu i v textu pr´ace. Mimo to bude k dispozici i mnoˇzina dalˇs´ıch klasick´ ych Prologovsk´ ych probl´em˚ u pro prezentaci z´ıskan´ ych znalost´ı a funkˇcnosti interpret´atoru samotn´eho. Shodn´ y obsah, vˇcetnˇe n´avodu k obsluze, bude k nalezen´ı na pˇriloˇzen´em CD. B-Prolog sk´ yt´a mnoh´a vylepˇsen´ı oproti standardu. Nejvˇetˇs´ı v´ yhodou je moˇznost propojen´ı s jazykem C. Nev´ yhodou je, ˇze nˇekter´e zabudovan´e predik´aty nefunguj´ı dle pˇr´ıruˇcky, ale to mohou m´ıt za n´asledek ˇcast´e aktualizace interpret´atoru, kter´ y se neust´ale vyv´ıj´ı. Pˇrestoˇze na ofici´aln´ıch webov´ ych str´ank´ach projektu a v licenc´ıch je uvedeno, ˇze B-Prolog je pro osobn´ı a akademick´e u ´ˇcely zdarma, zdrojov´e k´ody potˇrebn´e k propojen´ı s jazykem C k dispozici ke staˇzen´ı nejsou. Prostˇrednictv´ım elektronick´e komunikace s p. Zhou mi bylo sdˇeleno, ˇze zdrojov´e k´ody je moˇzn´e zpˇr´ıstupnit za poplatek $2,980, kter´ y vytvoˇril velkou pˇrek´aˇzku pˇri realizaci kombinovan´ ych program˚ u. Proto je tato problematika probr´ana pouze struˇcnˇe a teoreticky. Spojen´ı s jazykem Java je bohuˇzel v aktu´aln´ı verzi podporov´ano minim´alnˇe, a to pouze na platformˇe Windows 32-bit. Rozˇs´ıˇren´ı se u ´dajnˇe pl´anuje v n´asleduj´ıc´ıch verz´ıch B-Prologu. V´ yrobci se nyn´ı v´ıce soustˇred´ı na nov´ y projekt – Picat, kter´ y je nepˇr´ım´ ym n´asledovn´ıkem B-Prologu.
50
Literatura [1] Blackburn, P.; Bos, J. and Striegnitz, K.: Learn Prolog Now!. Texts in Computing, 2006, vol. 7. ISBN: 1-904987-17-6, s. 6-15. [2] Bramer, M.: Logic Programming with Prolog. University of Portsmouth, UK, 2005. ISBN-10: 1-85233-938-1, 223 s. [3] Covington, M. A.; Nute, D.; Vellino, A.: Prolog Programming in Depth. The University of Georgia, Athens, September 1993. Appendix A – ISO Prolog: A Summary of the Draft Proposed Standard, 1993. ISBN: 0-67318659-8, 27 s. [4] Covington, M. A.: Research Report AI-1989-02. A Numerical Equation Solver in Prolog. The University of Georgia, Athens, March 1989, 14 s. [5] Heng, Ch.: Free Prolog Compilers and Interpreters [online]. Last updated c March 24, 2014 1999-2014 [cit. 26. prosince 2013]. Dostupn´e z: http://www.thefreecountry.com/compilers/prolog. shtml [6] Kantrowitz. M.: Prolog Resource Guide [online]. Version: 1.36, Last c updated Feb 20, 1997. 1992-1994. [cit. 13. dubna 2014]. Dostupn´e z: http://www.cs.cmu.edu/afs/cs/project/ ai-repository/ai/html/faqs/lang/prolog/prg/top.html ´ [7] Kryl, R.: Neprocedur´aln´ı programov´an´ı: Uvod do programovac´ıho jazyka c PROLOG [online]. Verze 3.03. KSVI MFF UK, Praha. Rudolf Kryl, 2013 [cit. 13. dubna 2014]. Dostupn´e z: http://ksvi.mff.cuni.cz/∼kryl/prolog.pdf ´ [8] Skoupil, D.: Uvod do paradigmat programov´an´ı. Katedra matematick´e informatiky, Univerzita Palack´eho v Olomouci, 1997, s. 9-23
51
LITERATURA
LITERATURA
[9] Sterling, L.; Shapiro, E.: The Art of Prolog: advanced programming techniques. Massachusetts Institute of Technology, 1994. Appendix A – Operators. ISBN: 0-262-19338-8, s. 479-481 [10] Wielemaker, J. aj.: SWI Prolog Reference Manual. Herstellung und Verlag: Books on Demand GmbH, Norderstedt, 2012. ISBN 978-3-84-8226177, s. 59. c [11] Zhou, Neng-Fa: B-Prolog User’s Manual [online]. Version 7.8. Afany Software, 1994-2012. Last updated Jan 24, 2013 [cit. 26. prosince 2013]. Dostupn´e z: http://www.probp.com/download/manual.pdf
52