Prolog 2 (Filip Wroński, Łukasz Betkowski, Paweł Świerblewski, Konrad Kosmatka) Rozdział 2 Constructing Prolog Programs z książki Prolog Programming in Depth autorstwa Michael A. Covington, Donald Nute, André Vellino
Wstęp Prolog interpretuje klauzule jako definicje procedur. Wynika z tego, że język posiada semantyki deklaratywne i proceduralne. Jakakolwiek wiedza w Prologu może być rozumiana deklaratywnie jako reprezentowanie wiedzy, lub proceduralnie jako pewne określone akcje obliczeniowe.
Wstęp Ale nawet dla reprezentacji wiedzy, Prolog nie jest idealnie deklaratywny; programista musi pewne kwestie proceduralne przechowywać w głowie. Na przykład, niektóre poprawnie deklaratywnie bazy wiedzy tworzą nieskończone pętle. W innych przypadkach, dwie deklaratywnie równoważne bazy wiedzy mogą być całkowicie odmienne w kwestii efektywności obliczeniowej.
Wstęp W tym rozdziale skupimy się na proceduralnej interpretacji w Prologu. Poznamy wbudowany predykaty dla wejścia/wyjścia w celu modyfikowania bazy wiedzy i kontrolowania procesu wstecznego (backtracking). Programy w tym rozdziale będą zawierały bazę wiedzy jak i również zestawy procedur. Będziemy zazwyczaj używać prostej bazy wiedzy.
OUTPUT: polecenia write, nl, display Wbudowany predykat write traktuje każde termin Prologa jako własny argument i wyświetla go na ekranie. Predykat nl, który nie posiada argumentów, przechodzi do nowej linii.
Przykłady write, nl?- write('hello'), write('world'). Helloworld true?- write('hello'), nl, write('world'). Hello world true
Wielokrotne write Możemy używać wielokrotnie write w celu wypisania wartości przechowywanej przez zmienną, na przykład:?- matka(x,krystyna), write('matką krystyny jest '), write(x). Matką krystyny jest barbara. true
Właściwości write Jeśli argument jest niezainicjowaną zmienną, write wyświetli unikalny symbol identyfikujący tę zmienną, np. _0001.?- write(x).
writeq Zauważmy, że write wyświetla napisy bez cudzysłowów. Jeśli chcemy, aby write wypisało Hello world, wypisze Hello world, czyli w postaci dwóch jednostek, a nie jednej, co jest trudne w późniejszym ewentualnym wykorzystaniu przez składnię Prologa. Aby otrzymać napis razem z cudzysłowami, należy użyć predykatu writeq.
Przykład writeq?- writeq('hello there'). 'hello there' true
display Inny predykat, display wyświetla funktory z przodu ich argumentów, nawet jeśli były napisane w innych pozycjach. To sprawia, że display jest użyteczne dla poznania wewnętrznej reprezentacji danych w Prologu.
Przykład display?- display(2+2). +(2,2) true Jak widzimy, 2+2 nie przedstawia 4, tylko strukturę danych złożonych z 2, + i 2.
write_canonical Kolejny predykat, write_canonical, jest kombinacją writeq oraz display.?- write_canonical(2+3). +(2,3)?- write_canonical('hello there'). 'hello there'
Obliczanie i wydruk Ważne jest odróżnianie zapytań, które używają operacji wejścia/wyjścia, od zapytań, które ich nie używają. Na przykład:?- matka(x,karolina), write(x). Powyższe zapytanie mówi komputerowi, żeby znalazł, kto jest matką Karoliny i wypisał wynik.
Obliczanie i wydruk Dla kontrastu weźmy poniższe zapytanie:?- matka(x,karolina). To zapytanie z kolei mówi komputerowi, żeby zidentyfikował matkę Karoliny, ale z kolei nic nie mówi o wydruku. Jeśli wpiszemy kolejne zapytanie, żeby Prolog wypisał wynik, zostanie wypisana zmienna X, ponieważ Prolog zawsze wypisuje wartości zmiennych zainicjowanych przez zapytania, które nie użyły operacji wyjścia.
Obliczanie i wydruk Popularnym błędem jest konstruowanie predykatu, który ma coś wypisać, kiedy chcemy, żeby coś obliczył i na odwrót. W normalnych sytuacjach, dowolny predykat, który wykonuje obliczenia, powinien dostarczyć wynik za pomocą zainicjowanego argumentu, a nie za pomocą wypisania bezpośrednio na ekran. W ten sposób, wynik może być wykorzystany później w tym samym programie.
Wymuszanie wracania się z fail Wbudowany predykat fail można wykorzystać do wymuszenia innych predykatów do wracania się przez wszystkie rozwiązania.
Wymuszanie wracania się z fail Dla przykładu, rozważmy małą bazę wiedzy Stolice.?- stolica(kraj,miasto), write(miasto), write( jest stolicą ), write(kraj),nl. Powyższe zapytanie wyświetli informacje o pierwszym kraju, które znajdzie, ponieważ Prolog zakłada, że jeśli użyliśmy write, musi coś wypisać, niezależnie, czy chcieliśmy to widzieć.
Wymuszanie wracania się z fail W tym miejscu fail odgrywa swoją rolę. Aby wydrukować wszystkie alternatywy, możemy zbudować zapytanie w poniższy sposób:?- stolica(kraj,miasto), write(miasto), write( jest stolicą kraju ),write(kraj),nl,fail.
Wymuszanie wracania się z fail W miejscu predykatu fail możemy użyć dowolnego predykatu, który zwraca false, ponieważ dowolna porażka powoduje, że Prolog cofa się do ostatniej niewykorzystanej możliwości.
Wymuszanie wracania się z fail Jak to po kolei działa, obrazują kroki poniżej:?- stolica(kraj,miasto), write(miasto), write( jest stolicą kraju ),write(kraj),nl,fail. 1.Rozwiąż pierwszy cel, stolica(kraj,miasto) przez inicjalizację Kraj jako polska i Miasto jako warszawa. 2.Rozwiąż drugi, trzeci, czwarty i piąty cel (trzy razy write oraz nl) wypisując Warszawa jest stolicą kraju Polska i przejście do następnej linii. 3.Rozwiąż ostatni cel, fail. Ten cel nie może być rozwiązany, więc wróć. 4.Ostatni cel, który ma alternatywy, jest pierwszy, więc wybierz inny kraj oraz miasto i spróbuj ponownie.
Wymuszanie wracania się z fail Poniższe drzewo przedstawia ten proces. Widać wyraźnie, że na napotkaniu porażki, Prolog wraca na samą górę do polecenia stolica(kraj,miasto), żeby zdobyć alternatywę.