Prolog - wprowadzenie Wojciech Besler Paweł Madej
PrologProlog Prolog jest językiem programowania, którego początki sięgają 1970 roku. Był on używany przez wielu programistów zwłaszcza do: Relacyjnych baz danych Logiki matematycznej Rozwiązywania abstrakcyjnych problemów Analizowania struktur biochemicznych Wielu obszarów sztucznej inteligencji
Programowanie w języku Prolog nie polega na opisywaniu algorytmu, tak jak w pozostałych językach programowania. Programiści prologu zajmują się formalnymi relacjami i obiektami związanymi z danym problemem przez co język ten może być uważany za opisowy i deklaratywny. Programowanie w tym języku polega przede wszystkim na opisaniu znanych faktów i relacji dotyczących problemów, w mniejszym stopniu na podawaniu kolejnych kroków algorytmu. Jest to język programowania 5 generacji (języki sztucznej inteligencji) – najbardziej zbliżony do języka naturalnego.
Obiekty i relacje Prolog jest językiem programowania używanym do rozwiązywania problemów związanych z obiektami i relacjami między nimi. Stanowi on praktyczną i wydajną implementację szeregu aspektów „inteligentnego” wykonywania programu: Brak determinizmu Brak równoległości Brak wywoływania procedur według wzorca
Kiedy mówimy „Jan posiada książkę” deklarujemy relację (relationship), zależność która istnieje między dwoma obiektami – Janem, a książką. Ponadto w relacji ważna jest kolejność, która określa, że to Jan posiada książkę, a nie na odwrót. Zadając pytanie „Czy Jan posiada książkę?” próbujemy znaleźć relację. Wiele problemów może być wyrażonych za pomocą specyficznych obiektów i relacji między nimi. Możemy zmniejszyć ilość problemów poprzez odpytanie komputera o obiekty i relacje, które są w naszym programie. Przykład
Prolog zawiera ujednoliconą strukturę danych, na bazie której tworzone są wszystkie dane oraz programy. Programy składają się ze zbioru klauzul, które określają fakt opisujący sytuację lub są regułą mówiącą jak można powiązać fakt z rozwiązaniem problemu. W przeciwieństwie do np. C++ czy Javy, kiedy mówimy o obiektach, to nie chodzi o struktury danych składające się z pól i metod, ale o byty, które można opisać termami. Obiektem nazywamy coś, co jest bytem. Nie definiujemy z czego się on składa, ale jaki jest. Dla każdego obiektu definiujemy relacje jakim obiekt podlega. Najprostszym sposobem opisu problemu jest podanie faktów z nim związanych.
wieksze(szafa, biurko). ciezki(slon) szybszy(samochod, rower). Oznacza, że szafa jest większa od biurka, słoń jest ciężki, a samochód szybszy od roweru. Obiektami są szafa, biurko itd., z kolei „większy” i „szybszy” to relacje. Ważne jest to, że obiekty nie posiadają dokładnych cech (nie ma podanej np. wagi). Przykład
Gdybyśmy mieli uruchomić taki program, moglibyśmy zadawać pytania związane z rzeczywistością: ?-wiekszy(szafka, biurko) ?-ciezsze(szklanka, talerz) ?-szybszy(samochod, rower) W przypadku gdybyśmy zadali pytanie ?-szybszy(rower, samochod) otrzymalibyśmy odpowiedź „no” – która oznacza, że nie wiadomo czy rower jest szybszy od samochodu.
Na programowanie w Prologu składa się: Określanie pewnych faktów (facts) związanych z obiektami i ich relacjami Definiowanie zasad (rules) o obiektach i ich relacjach Tworzenie zapytań (questions) o obiekty i ich relacje Prolog może dużo więcej niż odpowiadanie na pytanie tak/nie. Wykorzystuje on komputer do trzymania danych odnośnie faktów i zasad, które prowadzą do logicznej dedukcji. ProgramowanieProgramowanie
Chcąc w Prologu napisać fakt „Andrzej lubi Anię” skorzystamy z dwóch obiektów: Andrzeja i Ani oraz relacji „lubienia”. Fakt ten zapiszemy w następującej postaci: lubi(andrzej, ania). Fakt musi spełniać następujące wymogi: Nazwy obiektów i relacji zapisywać małymi literami Najpierw zapisywana jest relacja, a następnie w nawiasie obiekty oddzielone przecinkiem Fakt musi kończyć się kropką Ważna jest kolejność argumentów Fakty (Facts) Przykład
Relacja może mieć dowolną liczbę argumentów. Jeżeli chcemy zdefiniować relację „czyta”, w którym podamy osoby i kategorię książki, w nawiasie podamy 3 argumenty. czyta(andrzej, anna, horror) Nazewnictwo: Obiekty w nawiasach są to argumenty – andrzej, anna, horror Nazwa relacji przed nawiasem to predykat - czyta Baza danych jest to zbiór faktów Nazwy obiektów i relacji są dowolne wartosciowe(zloto).Złoto jest wartościowe posiada(jane, zloto).Jane posiada złoto ojciec(marek, marta).Marek jest ojcem Marty Przykłady Przykład
Zapisany wcześniej program wczytujemy poleceniem: ?-[plik bez rozszerzenia]. Następnie formułujemy zapytanie: ?-posiada(anna,zloto). Chcemy znać odpowiedź na pytanie „Czy istnieje fakt, który stwierdza, że Anna posiada złoto?” Każde zapytanie musi posiadać symbol znaku zapytania i minusa (tzw. znak zachęty) oraz kropkę za nawiasem z argumentami Zapytania (Questions) Przykład
W odpowiedzi na zapytanie otrzymamy „yes” albo „no” – w drugim przypadku oznacza to, że nie znaleziono niczego co należy do zapytania. Warto dodać, że proces przeszukiwania odbywa się linia po linii od góry. Możemy również zamiast pytać czy Anna posiada złoto, zadać pytanie „Co posiada Anna?” ?-posiada(anna,X). posiada(kamil,ksiazka). posiada(kamil,auto). posiada(anna,zloto). posiada(anna,ksiazka). Zapytania: ?-posiada(kamil,ksiazka), posiada(anna,ksiazka). Czy prawdą jest, że Kamil posiada książkę i Anna posiada książkę? ?-posiada(kamil,X), posiada(anna,X). Co posiada zarówno Kamil jak i Anna? Przykłady
?-posiada(kamil,X) ; posiada(anna,X). Co posiada posiada Kamil, lub co posiada Anna?
Zamiast zadawać pytania w Prologu „Czy Kamil posiada książkę?” albo „Czy Anna lubi złoto?” lepiej pytać o wszystkie rzeczy np. Co lubi Kamil? Takie wyrażenie odczytujemy „Czy Kamil lubi X?” (Does Kamil like X?). Zadając takie pytanie nie wiemy jaki obiekt może kryć się pod symbolem X. Za to możemy dostać odpowiedź np. X=ksiazka Zmienna jest ciągiem znaków utworzonych z cyfr, małych i dużych liter, znaku podkreślenia. Należy zwrócić uwagę, że pierwszy symbol zmiennej musi być wielką literą lub znakiem podkreślenia. ZmiennaX _ZmiennaY _ X_Y Zmienne (Variables) Przykłady
Przy czym zmienna składająca się z samego symbolu podkreślenia, jest nazywana zmienną anonimową – korzysta się z niej, gdy interesuje Nas czy coś jest prawdą, ale nie musi Nas interesować co jest prawdą. Czy ktoś posiada złoto??-posiada(_,zloto). W przypadku, gdy jest wiele odpowiedzi, aby Prolog wyświetlił je wszystkie, należy wybrać znak średnika, a następnie enter. Jeżeli satysfakcjonuje Nas pierwszy lepszy wynik, wybieramy sam enter. posiada(kamil,ksiazka). posiada(kamil,auto). posiada(anna,auto). ?-posiada(kamil,X). Jeżeli wybierzemy enter, dostaniemy odpowiedź X=ksiazka, jeżeli chcemy poznać kolejną rzecz która posiada Kamil, wybierzemy średnik, a następnie enter. Przykład
Co się stanie mając powyższe fakty zadamy pytanie: ?-posiada (X, auto). Pytanie oznacza – czy jest jakiś obiekt, który posiada auto? Jak widać takimi elementami są Kamil i Anna, zatem znowu jeżeli chcemy zobaczyć wszystkie obiekty, które posiadają auto, wybieramy średnik, a następnie enter. ?-posiada(X,auto). X=kamil ; X=anna ; no
Czasami chcielibyśmy pisać bardziej skomplikowane zapytania takie jak: „Czy Marcin i Michał lubią gry?” Jednym ze sposobów na osiągnięcie wyniku jest sprawdzenie czy Marcin lubi gry, a następnie gdy Prolog zwróci odpowiedź „yes”, zapytać czy Michał lubi gry. W skrócie problem rozbijamy na mniejsze problemy. lubi(anna,czekolada). lubi(anna,wino). lubi(krystian,wino). lubi(krystian,anna). Chcemy dowiedzieć się czy Anna i Krystian lubią się nawzajem. W tym celu spytamy „Czy Krystian lubi Annę? i czy Anna lubi Krystiana? Możemy do tego użyć złączenia: ?-lubi(krystian,anna),lubi(anna,krystian) KoniunkcjeKoniunkcje Przykład
Koniunkcja w Prologu przedstawiona jest za pomocą przecinka. Rozdziela on elementy zapytania. W powyższym przykładzie Prolog zwróci odpowiedź „no” ponieważ pierwszy warunek jest spełniony (na podstawie lubi(krystian,anna) jest true), natomiast drugi warunek nie wiadomo czy jest spełniony ponieważ nie ma takiego faktu, który by to stwierdził. Zatem true & false daje false. lubi(anna,czekolada). lubi(anna,wino). lubi(krystian,wino). lubi(krystian,anna). Chcemy się dowiedzieć czy jest coś co Anna i Krystian lubią oboje? Sprawdzamy czy jest jakiekolwiek X, które lubi Anna Sprawdzamy czy ten X lubi Krystian Nasze zapytanie będzie zatem wyglądało: ?-lubi(anna,X), lubi(krystian,X). Przykład
Koniunkcja w Prologu przedstawiona jest za pomocą przecinka. Rozdziela on elementy zapytania. W powyższym przykładzie Prolog zwróci odpowiedź „no” ponieważ pierwszy warunek jest spełniony (na podstawie lubi(krystian,anna) jest true), natomiast drugi warunek nie wiadomo czy jest spełniony ponieważ nie ma takiego faktu, który by to stwierdził. Zatem true & false daje false. lubi(anna,czekolada). lubi(anna,wino). lubi(krystian,wino). lubi(krystian,anna). Chcemy się dowiedzieć czy jest coś co Anna i Krystian lubią oboje? Sprawdzamy czy jest jakiekolwiek X, które lubi Anna Sprawdzamy czy ten X lubi Krystian Nasze zapytanie będzie zatem wyglądało: ?-lubi(anna,X), lubi(krystian,X). Przykład
Prolog odpowiada na pytanie poprzez wyszukanie pierwszego satysfakcjonującego wyniku dla pierwszej części pytania. Jeżeli jest on w bazie, Prolog zaznacza to miejsce i próbuje znaleźć satysfakcjonujący wynik dla drugiej części zapytania. Jeżeli odnajdzie go w bazie, mamy rozwiązanie dla dwóch pytań – zatem kończymy działanie. Najważniejsze jest to, że pamięta miejsce, w którym znalazł odpowiadający element. Jeżeli drugi element koniunkcji nie byłby satysfakcjonujący, to Prolog wraca do pierwszego elementu i przeszukuje kolejne wyniki. W momencie kiedy Prolog znajdzie poprawną odpowiedź, zatrzymuje się i czeka na więcej instrukcji. Jeżeli wybierzemy ; enter Prolog będzie szukał dalszych wyników.
Przykład Poszczególne kroki
X = wine
Przypuśćmy, że chcemy ustanowić stan, w którym określimy, że Krystian lubi wszystkich ludzi. Jedną z możliwości jest napisanie: lubi(krystian,anna). lubi(krystian,monika). lubi(krystian,natalia). itd. Takie rozwiązanie byłoby czasochłonne, zwłaszcza, jeżeli takich osó byłoby bardzo dużo. Innym rozwiązaniem, które przyspieszy proces jest powiedzenie „Krystian lubi wszystkie obiekty, które należą do ludzi”. Jest to zasada, która mówi co Krystian lubi zamiast wypisywania wszystkich tych osób. Zatem reguły są tworzone, jeżeli mamy do czynienia z grupą różnych faktów – faktami połączonymi zależnościami. Do wyrażania zasad stosowane jest słowo „jeżeli (if)” Zasady (Rules)
Używam parasola jeżeli pada deszcz. Krystian zakłada okulary, jeżeli świeci słońce. Zasady są również używane do wyrażania definicji. X jest psem jeżeli: X szczeka i X ma cztery łapy. X jest siostrą Y jeżeli: X jest kobietą i X oraz Y mają tych samych rodziców Reguła składa się z części głównej i treści (head i body), które połączone są symbolem składającym się z dwukropka(:) i myślnika (-). Część główna składa się tylko z jednego predykatu, natomiast ciało może być koniunkcją dowolnej liczby warunków (oddzielonych przecinkami – oznaczającymi logiczne AND). Przykład
Chcemy zapisać, że Krystian lubi grać w gry, jeśli są za darmo i online. lubi(krystian,X) :- darmowe(X), online(X). Żeby reguła była spełniona muszą być spełnione jej podcele czyli gra musi być darmowa i musi być online. X jest siostrą Y jeżeli: X jest kobietą X ma matkę M i ojca O Y ma tą samą matkę i ojca co X Zapisujemy: siostra(X,Y):- kobieta(X), rodzice(X,M,O), rodzice(Y,M,O). lub siostra(X,Y):-kobieta(X),rodzice(X,M,O),rodzice(Y,M,O). Przykład
Struktury to trzeci rodzaj termów stosowany w Prologu. W standardzie języka nazywane są one „termami złożonymi”. Struktura to pojedynczy obiekt składający się z zestawu innych obiektów, nazywanych składnikami struktury. Struktury w Prologu zapisujemy podając funktor oraz jego składniki. funktor(składnik1,składnik2,składnik3) posiada(jan,ksiazka(wichrowe_wzgorza, autor(emily,bronte))) Powyższy przykład przeczytamy: Jan posiada książkę „Wichrowe wzgórza” autora Emily Bronte Struktura oraz fakt (relacja) jest taka sama. Predykat stosowany tutaj to funktor struktury. StrukturyStruktury Przykłady
Prolog rozróżnia dwie grupy znaków: drukowalne (czarne) i niedrukowalne (białe). Znaki drukowalne pojawiają się na monitorze, zaś niedrukowalne się nie pojawiają, za to powodują wykonanie czegoś: zostawienie odstępu (spacji), przejście do nowego wiersza i tym podobne. Oto wszystkie znaki drukowalne w Prologu : A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z ! ‘’ # $ % & ‘ ( ) = - ~ ^ | \ { } [ ] _ + ; * :,. ? / ZnakiZnaki Przykłady
Podstawowymi operatorami w Prologu są plus (+), minus (-), gwiazdka (*), ukośnik (/). Gdy zapisujemy je między swoimi argumentami, nazywamy je między operatorami infiksowymi, mogą one zostać zapisane jako prefiksowe oraz postfiksowe ! Zapis artmetyczny możemy zapisać za pomocą struktury (2,2) W Prologu zapis 2+2 nie jest równoważny z 4 jest to wyłącznie inny zapis struktury +(2,2). Każdy operator ma klasę priorytetu. Jest to liczba całkowita z zakresu 1 – 255, wyższy priorytet to ten bliższy jedynki. Mnożenie i dzielenie mają wyższy priorytet od dodawania i odejmowania. OperatoryOperatory Przykłady
Łączność operatorów jest stosowany gdy mamy kilka operatorów w takim samym priorytecie. 8/2/2 można zinterpretować jako (8/2)/2 lub 8/(2/2) Aby móc rozróżnić jak poprawnie należy zinterpretować łączenie operatorów oraz wiedzieć, które operatory są łączone lewostronnie a które prawostronnie. Operatory łączone lewostronnie muszą mieć po lewej operacje o priorytecie takim samym lub niższym, a po prawej o priorytecie niższym. Wszystkie operatory arytmetyczne dodawania, odejmowania, mnożenia oraz dzielenia są łączone lewostronnie. 8/4/4 (8/4)/4 5+8/2/2 5+((8/2)/2) Dla wygody oraz uniknięcia błędów zawsze można stosować nawiasy okrągłe. Przykłady
Predykat równości, infiksowy operator zapisywany jako znak =. Prolog stara się dopasować argumenty, a jeśli się to uda, cel jest uzgodniony. ?- X=Y. Prolog stara się dopasować X i Y, a jeśli się to uda cel jest uzgodniony. O unifikacji można myśleć jako o próbie uczynienia X i Y równymi. Jest to predykat wbudowany, czyli jest z góry zdefiniowany w Prologu. Jeśli mamy cel w postaci X=Y, gdzie X i Y są dowolnymi termami mogącymi zawierać zmienne nieukonkretnione, czy X i Y są równe, sprawdza się następująco: Jeśli X jest zmienną nieukonkretnioną, a Y nie jest powiązana z żadnym termem, to X i Y są równe. Poza tym X ukonkretniona jest wartością Y. ?- jedzie(student, rower) = X. Zapytanie nie zawiedzie, a zmienna X zostanie ukonkretniona strukturą jedzie(student, rower) RównośćRówność Przykłady
Liczby całkowite i atomy są sobie równe. elektryk = elektryk.uzgodniony papier = farby.zawodzi 1567 = 1567.uzgodniony 1568 = 7852.zawodzi Przykłady
Jeśli struktury mają taki sam funktor oraz taką samą liczbę składników, zaś odpowiadające sobie składniki są sobie równe, to te struktury są sobie równe. jedzie (student, rower) = jedzie(student, X). Powyższe zapytanie nie zawiedzie, a X zostanie ukonkretniona wartością rower. Struktury mogą być zagnieżdżane, jeśli takie zagnieżdżone struktury są porównywane, porównywanie może trwać do czasu sprawdzenia wszystkich stuktur. a(c,C,d(e,F,g(h,i,J))) = a(B,c,d(E,f,g(H,i,j))). Cel zostanie uzgodniony, B zostanie ukonkretnione jako b, C jako c, E jako e, F jako f… Przykłady
Jeżeli porównamy dwie nieukonkretnione zmienne, to cel zostanie uzgodniony. Obie zmienne zostaną powiązane i gdy jedna zmienna zostanie ukonkretniona jakimś termem, druga automatycznie zostanie ukonkretniona tym samym termem.
Predykaty do porównywania liczb to =:=, =\=,, = =. X =:= Y X i Y są tą samą liczbą X =\= Y X i Y są różnymi liczbami X X jest mniejsze od Y X > Y X jest większe od Y X = X jest mniejsze lub równe od Y X >= Y X jest większe lub równe od Y ArytmetykaArytmetyka Przykłady
Operator infiksowy „is” stosujemy przy wyliczeniach operacji arytmetycznych. Jego prawym argumentem jest term, który ma być zinterpretowany jako wyrażenie arytmetyczne. Wynik jest dopasowany do lewego argumentu. Wynik = ZmiennaA + ZmiennaB. Do prawego argumentu możemy zastosować następujące operatory arytmetyczne. X + Ysuma X i Y X - Yróżnica X i Y X * Yiloczyn X i Y X / Yiloraz X przez Y X // Ycałkowity iloraz X przez Y X mod Yreszta z dzielenia X przez Y Operatory Arytmetyczne Przykłady
Prolog realizuje zadania w odpowiedzi na zapytania programisty. Zapytanie to koniunkcja celów, które maja być spełnione. W Prologu do spełnienia celów używa się znanych klauzul. Fakt może spowodować, że cel będzie spełniony od razu, zaś reguła pozwala zadanie spełniać stopniowo, przez spełnianie koniunkcji jej podcelów. Spełnienie celów
Cel można zunifikować z głową klauzuli w kilku przypadkach : Nieukonkretniona zmienna unifikuje się z dowolnym obiektem. W wyniku tego zmienna będzie zastąpiona przez ten obiekt. Jeśli zmienna nie jest nieukonkretniona, liczba całkowita lub atom unifikują się jedynie z samymi sobą. Jeśli nie zachodzi żaden z powyższych przypadków, struktura unifikuje się z inną strukturą o takim samym funktorze i takiej samej liczbie argumentów, zaś odpowiadające sobie argumenty też muszą się unifikować. UnifikacjaUnifikacja