====== LAB: Pisanie programów w Prologu ====== ===== -. Interakcja z programem ===== ==== Wypisywanie na wyjściu ==== Predykat //write/1// wypisuje term na wyjście; //nl/0// przechodzi do nowej linii. **Ćwiczenie** Przetestować działanie: write('Ala ma '),write('kota'),nl,write('w ciapki!'). ==== Programy interaktywne ==== Predykat read/1 pozwala na pobieranie danych od użytkownika. **Ćwiczenie** Proszę oglądnąć zastosowanie //read/1// na przykładzie programu {{interac.pl}} Po załadowaniu proszę wpisać: ?- go. ===== -. Wymuszanie nawrotów ===== Predykat //fail/0// pozwala na wymuszanie nawrotów w procesie poszukiwania rozwiązania, co pozwala w szczególności na znalezienie wszystkich rozwiązań problemu. Przypomnienie: Prolog używa strategii przeszukiwania wgłąb drzewa rozwiązań problemu i zatrzymuje sie po napotkaniu 1. poprawnego rozwiązania. **Ćwiczenie** Pobrać i wczytać program {{fam2.pl}} Wyświetlić go przez listing. Sprawdzić działanie: ?- kobieta(K),write(K),write(' to kobieta.'),nl. Jak wymusić odnajdywanie kolejnych kobiet. Sprawdzić działanie: ?- kobieta(K),write(K),write(' to kobieta.'),nl,fail. Sprawdzić działanie: ?- kobieta(K),fail. Dlaczego nic się nie pojawia? Pobrać i wczytać program {{capitals.pl}} Sprawdzić działanie: ?- capital_of(A,B), write(B), write(' to stolica '), write(A), nl. ?- capital_of(A,B), write(B), write(' to stolica '), write(A), nl, fail. ===== -. Dynamiczna bazy wiedzy ===== ==== Modyfikacja bazy wiedzy ==== Predykaty //assert/a/z /1//, //retract/a/z /1// pozwalają na dodawanie, usuwanie faktów do/z bazy wiedzy, na/do jej początku/końca. Predykat //abolish/1// pozwala usunąć predykat z bazy wiedzy. (np. ''abolish(kobieta/1).'') Predykat //retractall/1// pozwala usunąć klauzule danego predykatu z bazy wiedzy. (np. ''retractall(kobieta(K)).'') Predykaty //see/n//, //tell/told// pozwalają na odczyt, zapis bazy wiedzy z/do pliku. **Ćwiczenie** Proszę napisać: ?- assert(kobieta(kopernik)). jak zmieniła sie wiedza na temat kobiet? ?- listing(kobieta). Proszę sprawdzić: kobieta(K),write(K),write(' to kobieta.'),nl,fail. Uwaga: niektóre kompilatory Prologu (np. SWI) wymagają wcześniejszego zadeklarowania predykatu jako takiego, który może być dynamicznie modyfikowany. Robi się to przez predykat //dynamic/1//, np. dla predykatu //kobieta/1// deklaracja w pliku //fam2.pl// ma postać '':- dynamic(kobieta/1).''. Proszę oglądnąć zastosowanie //assert/retract// na przykładzie programu {{learner.pl}} Początkowa baza wiedzy jest w pliku {{learner_kb.pl}} Należy uruchomić program przez ''start.'' Jakie 3 przypadki odpowiedzi są brane pod uwagę? Co dzieje się przy wyjściu z programu i jak to wpływa na jego kolejne uruchamianie? Uwaga: operator ''\+'' oznacza //negację//. Predykat //dynamic/1// jest potrzebny w niektórych implementacjach Prologu (np. SWI) do umożliwienia wykonywania dynamicznych modyfikacji, t.j. //assert// i //retract//. ==== Modyfikacja reguł ==== assert/retract pozwala również dodawać/usuwać reguły: :-dynamic(a/1), dynamic(b/2). a(1). a(2). addrule:- assert((b(X,Y):-a(X),a(Y))). delrule:- retract((b(_,_):-_)). start:-addrule, b(X,Y), write(X), write(' '), write(Y),nl,fail. start. W powyższym programie predykat ''addrule/0'' dodaje (''delrule/0'' usuwa) dynamicznie regułę: b(X,Y):-a(X),a(Y). ===== -. Zadawanie celu ===== Konstrukcja: :- cos. pozwala na podanie celu w programie, np.: :- dynamic(capital_of/2). :- go. :- start. Prolog przystąpi do zrealizowania celu po załadowaniu kodu. Uruchomienie programu ze wskazanego pliku (''plik.pl'') z poziomu systemu operacyjnego można zrealizować za pomocą: swipl -s program.pl -g go -t halt ===== -. Śledzenie pracy programu ===== Interpretery Prologu pozwalają na śledzenie pracy programu. W tym celu należy ustawić tzw. trace point. Trace points: * trace/0 - ustawia trace point na wszystkich predykatach, w wersji swi-prolog używanej na laboratorium, ''trace/0'' śledzi **następny zadany cel**, np. ?- trace,kobieta(K),rodzic(K,_). * trace/1 - ustawia trace point na wskazanym predykacie, * trace/2 - modyfikuje dla wskazanego predykatu śledzone zdarzenia (call, redo, exit, fail), np.: * trace(something/1,-all) - zdejmuje trace point ze wskazanego predykatu //something/1//, * trace(something,+call) - ustawia trace point śledzący jedynie wywołania na wskazane predykaty //something// o dowolnej arności. Predykat //debugging/0// wypisuje ustawione trace points. Predykat //debug/0// wchodzi do trybu debug. Predykat //nodebug/0// wychodzi z trybu debug. **Ćwiczenie** Pobrać i wczytać program {{fam2.pl}} Sprawdzić działanie: ?- trace(matka). [debug] ?- matka(kasia,robert). [debug] ?- ojciec(tomek,robert). [debug] ?- nodebug. ?- matka(kasia,robert). ?- debug. [debug] ?- trace(matka,-all). [debug] ?- matka(kasia,robert). [debug] ?- trace(matka,+call). [debug] ?- matka(kasia,robert). [debug] ?- nodebug. ?- trace. [debug] ?- matka(kasia,robert). [debug] ?- nodebug. W SWI Prologu można też skorzystać z dodatkowego pakietu XPCE, w którym jest też wizualny debugger. Należy wyjść z bieżacej sesji SWI. Uruchomić interpreter z powłoki unixa przez przez polecenie ''xpce''. Następnie uruchomić graficzny tracer przy użyciu predykatu guitracer/0. ===== -. Arytmetyka w Prologu ===== W Prologu nie można w sposób //bezpośredni// wykonywać obliczeń arytmetycznych. Służy do tego predykat //is//. **Ćwiczenie** 1. Sprawdzić działanie: ?- X is 2 + 2. ?- Y is 2.5 + ( 4 / 2). ?- Z is 2 + 0.001. Uwaga: ?- A is 3. ?- B is A + 4. ?- A is 3, B is A + 4. Operacje arytmetyczne: ?- X is 2 + 2. ?- X is 2 * 3. ?- X is 4 / 2. ?- X is 4 / 3. ?- X is 4 // 3. Uwaga na //podstawianie//: ?- X is 2 + 5. ?- X = 2 + 5. ?- 2 + 5 =:= 1 + 4. ?- 2 + 5 =:= 3 + 4. ?- 2 + 5 =:= 4 + 4. Przećwiczyć użycie operatorów: ?- 2 < 3. ?- 2 > 3. ?- 3 > 3. ?- 3 >= 3. ?- 3 =< 3. 2. Napisz program obliczający wynik [[http://pl.wikipedia.org/wiki/R%C3%B3wnanie_kwadratowe|równania kwadratowego]] ([[wp>Quadratic Equation]]) ''ax^2 + bx + c = 0'' w dziedzinie liczb rzeczywistych. Zaimplementuj predykaty: * ''delta/4'' -- obliczający deltę, argumenty kolejno: a, b, c, wynik, * ''kwadrat/4'' -- obliczający wynik równania kwadratowego, argumenty kolejno: a, b, c, wynik. Zwróć uwagę na niedeterminizm w predykacie ''kwadrat/4'', który znajduje zero, jedno, albo dwa rozwiązania; mogą się przydać [[http://www.swi-prolog.org/pldoc/man?section=arith|funkcje matematyczne]], w szczególności do obliczenia pierwiastka używa się ''sqrt/1''. ===== -. Rekurencja w Prologu ===== Typowym przykładem wykorzystania mechanizmu rekurencji jest obliczanie wartości funkcji silnia. Poniżej znajduje się kod realizujący tą funkcjonalność. factorial(0,1). factorial(Number,Result) :- Number > 0, NewNumber is Number-1, factorial(NewNumber,NewResult), Result is Number*NewResult. **Ćwiczenie** Wpisz, przetestuj i przemyśl działanie programu rekurencyjnie liczącego silnię. Uruchom program w trybie śledzenia wykonywania (trace). **Ćwiczenie** Opierając się na silni napisz program wypisujący [[http://pl.wikipedia.org/wiki/Ci%C4%85g_Fibonacciego|Ciąg Fibonacciego]]. ===== - Wybrane problemy rozwiązane w Prologu ===== ==== Świat klocków ==== Problem [[wp>Blocks_world]] Wczytaj {{:pl:prolog:prolog_lab:blocks.pl|}} Narysuj na kartce zamodelowany świat. Jakie pytania można zadać? above(b1,b2). above(b3,b5). left(b1,b7). left(b3,b3). **Ćwiczenie** Opisz w programie taki świat: a1 a2 a3 c1 c3 a4 a5 c2 c4 ==== Wieże Hanoi ==== Problem [[http://pl.wikipedia.org/wiki/Wieże_Hanoi]]. Problem [[wp>Towers_of_hanoi]] **Ćwiczenie** Proszę pobrać program {{:pl:prolog:prolog_lab:hanoi.pl|hanoi.pl}}. Przetestować i przemyśleć. Predykat //move/4// działa następująco:\\ np. //move(3,left,right,center)// przenosi 3 krążki ze stosu //left// na stos //right// przy pomocy stosu //center//. Przykład: ?- move(3,left,right,center). Move top disk from left to right Move top disk from left to center Move top disk from right to center Move top disk from left to right Move top disk from center to left Move top disk from center to right Move top disk from left to right Analizując kod programu proszę zauważyć, że algorytm rozwiązania problemu opiera się na dekompozycji na podproblemy. ==== Kolorowanie Mapy ==== Problem: mamy mapę taką jak poniżej |Bialorus |------------ Polska | ---------------| | | Ukraina Czechy| Slowacja|----------- ----------------- Należy ja pokolorować 3 kolorami, tak aby żadne sąsiadujące państwa nie miały takiego samego koloru: [[wp>Four_color_theorem]] **Ćwiczenie** Definiujemy 3 kolory: kolor(czerwony). kolor(zielony). kolor(niebieski). Należy zdefiniować predykat ''koloruj/5'', tak aby zadając pytanie: ?- koloruj(Polska,Bialorus,Ukraina,Slowacja,Czechy). dostać wszystkie możliwości pokolorowania tej konkretnej mapy. Uwaga: predykat ''koloruj/5'' definiuje zależności geograficzne. Uwaga: w Prologu operator ''\='' to nieidentyczność, czy też niemożliwość uzgodnienia termów. ==== Dla Zainteresowanych ==== Więcej ciekawych problemów [[reprezentacja_wiedzy#ciekawe_problemy|na kolejnych zajęciach]] oraz w [[prolog:pllib:start|bibliotece programów]]. ===== Obserwacje ===== W Prologu negacja opiera się o //Closed World Assumption//! W Prologu nie ma dualizmu "dane/kod" -> w Prologu jest jedna //baza wiedzy//, zawierająca fakty i reguły, którą można dynamicznie modyfikować. ===== Komentarze ===== Z braku lepszego miejsca tutaj studenci wpisują komentarze natury ogólnej do tego lab. 8-) --- //[[gjn@agh.edu.pl|Grzegorz J. Nalepa]] 2008/02/20 14:34//