Programowanie w logice Prolog 2
Listy Lista to uporządkowany ciąg elementów. Elementami listy mogą być dowolne terminy: stałe, zmienne i struktury W Prologu listę zapisujemy następująco: Przykłady [element1,element2,,elementn] [a,b,cd,df,p,w] [ala,ma,kota,[a,x],y] [ ] Lista pusta
Listy Każda lista składa się z: głowy (ang. head) pierwszy element listy, ogona (ang. tail) będącego zawsze listą. Przykłady [a,b,cd,df,p,w] - głowa: [a], ogon: [b,cd,df,p,w] [ala,ma,kota,[a,x]] - głowa: [ala], ogon: [ma,kota,[a,x]] [ ] - głowa: [ ], ogon: [ ] Listę o głowie X i ogonie Y zapisujemy: [X Y]
Listy Ważne: Głową listy może być dowolny obiekt języka Prolog np. inna lista, zmienna. Ogon listy jest zawsze listą (może być listą pustą []). Lista jest strukturą rekurencyjną - jeżeli ogon jest niepusty, to również on składa się z głowy i z ogona.
Listy Rozważmy listę: [element1,element2,,elementn] Reprezentacja wewnętrzna listy odpowiada strukturze drzewiastej: funktor termu tworzącego listę element1 element2 element3 elementn [] lista pusta
Listy Przykład?- [a,b,c]=[a,b,c]. true.?- [a,b,c]=[x,b,c]. X = a.?- [a,b,c]=[x,b,y]. X = a, Y = c.?- [a,b,c]=[x Y]. X = a, Y = [b, c].?- [a,b,c]=[x _]. X = a.?- [a,b,c]=[x g,h].
Listy i rekurencja Rozważmy listę: [audi,ford,fiat,renault,opel,chrysler,chevrolet] Załóżmy, że chcemy dowiedzieć, się czy jakaś marka samochodu jest elementem powyższej listy? W Prologu zaczynamy sprawdzać od głowy listy. Jeżeli odpowiedź jest negatywna sprawdzamy czy element należy do ogona listy. Ogon listy jest także listą więc znowu zaczynamy od głowy listy będącej ogonem aż dojdziemy do listy pustej.
Sprawdzenie przynależności Definicja : X należy do listy L, o ile X jest głową listy L (punkt A) lub X należy do ogona listy L (punkt B). Formalna definicja: (A) nalezy(x,[x _]). (B) nalezy(x,[_ Yogon]):-nalezy(X,Yogon). Przykład:?- nalezy(a,[b,c,d]).?- nalezy(a,[a,c,d]). true ;
Sprawdzenie przynależności nalezy(x,[x _]). nalezy(x,[_ Yogon]):-nalezy(X,Yogon). Wówczas:?- nalezy(a,[b,c,d]).?- nalezy(a,[a,c,d]). true ;?- nalezy(x,[a,c,d]). X = a.?- nalezy(x,[a,[a,v]]). X = a ; X = [a, v] ;
Sprawdzenie przynależności cd nalezy(x,[x _]). nalezy(x,[_ Yogon]):-nalezy(X,Yogon). Wówczas:?- nalezy(x,[_ a,b,c]). true ;?- nalezy(x,[a _]). X = a ; true ; true ; true ; true ; true ; true zapętlenie
Łączenie (konkatenacja) Chcemy zdefiniować predykat: sklej(l1,l2,l3). Lista L3 jest sklejeniem list L1 i L2. Definicja : Jeżeli lista L1 jest pusta, to lista L3 jest taka sama jak lista L2. Jeżeli lista L1 jest niepusta i ma postać [H T1], to lista L3 ma postać [H T2], gdzie T2 jest połączeniem list T1 i L2.
Łączenie (konkatenacja) Formalna definicja: sklej([],l,l). sklej([h T1],L2,[H T2]):-sklej(T1,L2,T2). Graficznie: L1 H T1 L2 T2 H T1 L2 [H T2]
Łączenie (konkatenacja) Przykład:?- sklej([a],[b,c],[a,b,c]). true.?- sklej([a],[b,c],[a,b,c,d]).?- sklej([a],[b,c],x). X = [a, b, c].?- sklej(y,[b,c],x). Y = [], X = [b, c] ; Y = [_G377], X = [_G377, b, c] ; Y = [_G377, _G383], X = [_G377, _G383, b, c] ; Y = [_G377, _G383, _G389], X = [_G377, _G383, _G389, b, c].
Zastosowanie konkatenacji Chcemy inaczej zdefiniować predykat: nalezy(x,l). Powyższa klauzula jest prawdziwa jeżeli element X należy do listy L. Definicja : X należy do listy L, o ile L jest konkatenacją listy L1 i listy [X,L2].
Zastosowanie konkatenacji Formalna definicja: lub: nalezy(x,l):-sklej(l1,[x L2],L). nalezy(x,l):-sklej(_,[x _],L). Graficznie: L L1 X L2
Dodanie elementu Chcemy zdefiniować predykat: dodaj(x,l1,l2). Do listy L1 dodajemy jako głowę element X i otrzymujemy listę L2. Definicja : Głową listy L2 jest element X, ogonem lista L1.
Dodanie elementu Formalna definicja: Przykład: dodaj(x,l,[x L])?- dodaj(a,[b,c,dfgf],x). X = [a, b, c, dfgf].?- dodaj(a,[b,c,dfgf],[a,b,c,dfgf]). true.?- dodaj(x,[b,c,dfgf],[a,b,c,dfgf]). X = a.?- dodaj(x,[b,c,dfgf],y). Y = [X, b, c, dfgf].
Usunięcie elementu Chcemy zdefiniować predykat: usun(x,l1,l2). Powyższa klauzula jest prawdziwa jeżeli lista L2 powstaje przez usunięcie elementu X z listy L1. Definicja : Jeżeli X jest głową listy L1, to lista L2 jest ogonem listy L1. Jeżeli X należy do ogona listy L1, to usuń stamtąd X.
Usunięcie elementu Formalna definicja: usun(x,[x O],O). usun(x,[y L],[Y O]):-usun(X,L,O). Graficznie: L Y X O [Y L] Y X O [Y O]
Usunięcie elementu Przykład:?- usun(a,[b,c,dfgf],y).?- usun(a,[b,a,dfgf],y). Y = [b, dfgf].?- usun(a,[b,a,d],y). Y = [b, d]?- usun(a,[b,a,d,a],y). Y = [b, d, a] ; Y = [b, a, d] ;?- usun(a,x,[b,c]). X = [a, b, c] ; X = [b, a, c] ; X = [b, c, a] ;
Lista odwrotna Przykład:?- odwr([a,b,c,d],[d,c,a,b]).?- odwr([a,b,c,d],[d,c,b,a]). true.?- odwr([a,b,c,d],x). X = [d, c, b, a].
Podlisty Chcemy zdefiniować predykat: podlist(l1,l2). Powyższa klauzula jest prawdziwa jeżeli lista zawiera się w liście L2. L1 Definicja : Lista S należy do listy L, o ile lista L składa się z dwóch list L1 i L2, a lista L2 jest połączeniem list S i L3.
Podlisty Formalna definicja: podlist(s,l):-sklej(l1,l2,l),sklej(s,l3,l2). Graficznie: L L1 S L3 L2
Podlisty Przykład:?- podlist([a],[a,b]). true.?- podlist([a,c],[a,b,c,d]).?- podlist([a,b,c],[a,b,c,d]). true.?- podlist([c,d],[a,b,c,d]). true.?- podlist(x,[a,b]). X = [] ; X = [a] ; X = [a, b] ; X = [] ; X = [b] ; X = [] ;
Permutacja listy Chcemy zdefiniować predykat: permut(l1,l2). Powyższa klauzula jest prawdziwa, jeśli lista L2 jest permutacją listy L1. Definicja : Jeżeli pierwsza lista (L1) jest pusta, to druga lista (L2) również jest pusta. Najpierw usuwamy element X, na pozostałej reszcie L1 dokonujemy permutacji i wstawiamy element X na początek poddanej już permutacji reszcie listy (czyli P).
Permutacja listy Formalna definicja: permut([],[]). permut(l,[x P]):-usun(X,L,L1),permut(L1,P). Graficznie: L X L1 permutacja P
Permutacja listy Przykład:?- permut([a,b,c],[a,c,b]). true.?- permut([a,b,c],[a,c,d]). false.?- permut([a,b,c],x). X = [a, b, c] ; X = [a, c, b] ; X = [b, a, c] ; X = [b, c, a] ; X = [c, a, b] ; X = [c, b, a] ;
Mechanizm nawracania Rozważmy zapytanie:?- lubi(tomek,x),lubi(ala,x) Kolejne etapy znajdowania rozwiązania: 1. Uzgodniony jest cel pierwszy zmienna X przyjmuje wartość ryby.?- lubi(tomek,x),lubi(ala,x). ryby ryby lubi(tomek,ryby). lubi(tomek,ksiazka). lubi(ala,ksiazka). lubi(tomek,ala).
Mechanizm nawracania 2. Następuje próba uzgodnienia drugiego celu. 3. Drugi cel zawodzi.?- lubi(tomek,x),lubi(ala,x). ryby ryby lubi(tomek,ryby). lubi(tomek,ksiazka). lubi(ala,ksiazka). lubi(tomek,ala). 3. Następuje nawrót poprzednia wartość zmiennej X jest odrzucona. Próbujemy ponownie uzgodnić pierwszy cel.
Mechanizm nawracania 4. Ponownie uzgodniony jest cel pierwszy zmienna X przyjmuje wartość ksiazka.?- lubi(tomek,x),lubi(ala,x). ksiazka ksiazka lubi(tomek,ryby). lubi(tomek,ksiazka). lubi(ala,ksiazka). lubi(tomek,ala). 5. Ponownie następuje próba uzgodnienia drugiego celu.
Mechanizm nawracania 6. Drugi cel jest uzgodniony.?- lubi(tomek,x),lubi(ala,x). ksiazka ksiazka lubi(tomek,ryby). lubi(tomek,ksiazka). lubi(ala,ksiazka). lubi(tomek,ala). 7. Prolog informuje o udanym uzgodnieniu koniunkcji i czeka na naszą reakcję.
Mechanizm nawracania Przykład:?- lubi(tomek,x),lubi(ala,x). X = ksiazka;..ale przypadku bazy: otrzymamy: lubi(tomek,ryby). lubi(tomek,ksiazka). lubi(ala,ksiazka). lubi(ala,ryby). lubi(tomek,ala).?- lubi(tomek,x),lubi(ala,x). X = ryby ; X = ksiazka ; Ponowne uzgodnienie celu nie powiodło się!
Mechanizm nawracania Rozważmy następującą definicję funkcji: Wykres:
Mechanizm nawracania Definicja funkcji w Prologu: Przykład: f(x,2):-x=<2. f(x,1):-2<x,x=<4. f(x,0):-4<x.?- f(5,x). X = 0.?- f(3,x). X = 1 ;?- f(-1,x). X = 2 ; Ponowne uzgodnienie celu nie powiodło się!
Mechanizm nawracania Przeanalizujmy szczegółowo powyższy przykład:?-?- f(5,x). 2 Ale 5>2!!! f(x,2):-x=<2. f(x,1):-2<x,x=<4. f(x,0):-4<x. Cel nieosiągnięty!!!?-?- f(5,x). 1 Ale 5>4!!! f(x,2):-x=<2. f(x,1):-2<x,x=<4. f(x,0):-4<x. Cel nieosiągnięty!!!
Mechanizm nawracania Przeanalizujmy szczególnie powyższy przykład:?-?- f(5,x). W efekcie otrzymujemy: 0 OK 5>4!!! f(x,2):-x=<2. f(x,1):-2<x,x=<4. f(x,0):-4<x. Cel osiągnięty!!!?- f(5,x). X = 0. a ponieważ to ostatnia reguła nic więcej nie otrzymujemy!
Mechanizm nawracania A teraz inne zapytanie:?-?- f(3,x). 2 Ale 3>2!!! f(x,2):-x=<2. f(x,1):-2<x,x=<4. f(x,0):-4<x. Cel nieosiągnięty!!!?-?- f(3,x). 1 OK 2<3<=4 f(x,2):-x=<2. f(x,1):-2<x,x=<4. f(x,0):-4<x. Cel osiągnięty!!!
Mechanizm nawracania W efekcie otrzymujemy:?- f(3,x). X = 1; Ale nie doszliśmy jeszcze do ostatniej reguły w bazie. Wybieramy zatem ; czyli próbujemy ponownie osiągnąć cel.?-?- f(3,x). 1 Ale 3<4 f(x,2):-x=<2. f(x,1):-2<x,x=<4. f(x,0):-4<x. Cel nieosiągnięty!!!
Mechanizm nawracania To była ostatnia reguła w bazie zatem otrzymujemy:?- f(3,x). X = 1 ; Cel osiągnięty!!! Nieudana próba ponownego osiągnięcia celu!!! Z podobną sytuacją mamy do czynienia w przypadku zapytania:?- f(-1,x). X = 2 ; Cel osiągnięty!!! Nieudana próba ponownego osiągnięcia celu!!!
Mechanizm nawracania Zauważmy, że: W rozważanej definicji funkcji mamy do czynienia z trzema rozłącznymi warunkami (z trzema przedziałami). Jeżeli spełniony jest któryś z warunków wówczas nie ma potrzeby sprawdzać warunków pozostałych. Mechanizm nawracania prowadzi do sytuacji w której po osiągnięciu celu rozważane są przypadki, które nie mogą być spełnione. Rozwiązaniem jest tzw. mechanizm cięć.
Mechanizm odcięć Odcięcie symbolizuje cel, który jest natychmiast spełniony, gdy tylko zostanie osiągnięty znak odcięcia czyli!. Wszystkie cele, które zostały do tej chwili spełnione nie będą analizowane powtórnie w celu sprawdzenia alternatywnych sposobów ich spełnienia).
Mechanizm odcięć Alternatywna definicja rozważanej funkcji: f(x,2):-x=<2,!. f(x,1):-2<x,x=<4,!. f(x,0):-4<x. Przykład:?- f(5,x). X = 0.?- f(3,x). X = 1.?- f(-1,x). X = 2.?- f(5,x). X = 0.?- f(3,x). X = 1 ;?- f(-1,x). X = 2 ; (bez cięć)
Mechanizm odcięć Rozważmy ponownie zapytanie:?-?- f(3,x). 2 Ale 3>2!!! f(x,2):-x=<2,!. f(x,1):-2<x,x=<4,!. f(x,0):-4<x. Cel nieosiągnięty!!!?-?- f(3,x). 1 OK 2<3<=4 f(x,2):-x=<2,!. f(x,1):-2<x,x=<4,!. f(x,0):-4<x. Cel osiągnięty, ODCIĘCIE!!!
Mechanizm odcięć Rozpatrzmy definicję liczby maksymalnej spośród dwóch liczb: max(x,y,x):-x>=y,!. max(x,y,y). Przykład:?- max(2,5,y). Y = 5.?- max(12,5,y). Y = 12.?- max(5,5,y). Y = 5.?- max(12,5,y). Y = 12 ;?- max(2,5,y). Y = 5.?- max(5,5,y). Y = 5 ; (bez cięć)
Mechanizm odcięć Rozpatrzmy definicję przynależności do listy z wykorzystaniem ocięcia: nalezy(x,[x _]):-!. nalezy(x,[_ Yogon]):-nalezy(X,Yogon). Przykład:?- nalezy(a,[b,c,d]).?- nalezy(a,[c,a,d]). true.?- nalezy(a,[c,a,a]). true.?- nalezy(a,[b,c,d]).?- nalezy(a,[c,a,d]). true ; (bez cięć)?- nalezy(a,[c,a,a]). true ; true ;
Dodanie elementu do listy bez powtórzeń Chcemy zdefiniować predykat: dodaj(x,l1,l2). Do listy L1 dodajemy jako głowę element X (jeżeli elementu tego nie ma na liście L1) i otrzymujemy listę L2. Definicja : Jeżeli X należy do listy L, to L1=L w przeciwnym przypadku L1 jest równe L powiększonemu o X.
Dodanie elementu do listy bez powtórzeń Formalna definicja: dodaj(x,l,l):- nalezy(x,l),!. dodaj(x,l,[x L]). Przykład:?- dodaj(a,[c,a,a],x). X = [c, a, a].?- dodaj(a,[b,c,d],x). X = [a, b, c, d].?- dodaj(a,[b,c,a,d],x). X = [b, c, a, d]. (bez cięć)?- dodaj(a,[c,a,a],x). X = [a, c, a, a].?- dodaj(a,[b,c,d],x). X = [a, b, c, d].?- dodaj(a,[b,c,a,d],x). X = [a, b, c, a, d].
Mechanizm odcięć Rozpatrzmy teraz bazę danych: ojciec(filip,szymon). ojciec(filip,marek). dziecko(kasia,szymon). dziecko(julia,szymon). dziecko(joasia,marek). dziadek(x,z):- ojciec(x,y),dziecko(z,y). Wówczas:?- dziadek(filip,x). X = kasia ; X = julia ; X = joasia.
Mechanizm odcięć Rozpatrzmy teraz bazę danych: ojciec(filip,szymon). ojciec(filip,marek). dziecko(kasia,szymon). dziecko(julia,szymon). dziecko(joasia,marek). dziadek(x,z):- ojciec(x,y),!,dziecko(z,y). Wówczas:?- dziadek(filip,x). X = kasia ; X = julia ;
Mechanizm odcięć Rozpatrzmy teraz bazę danych: ojciec(filip,szymon). ojciec(filip,marek). dziecko(kasia,szymon). dziecko(julia,szymon). dziecko(joasia,marek). dziadek(x,z):- ojciec(x,y),dziecko(z,y),!. Wówczas:?- dziadek(filip,x). X = kasia.
Mechanizm odcięć formalnie Rozważmy klauzulę: A:- B 1,B 2,...,B n,!,b n+2,...,b m. W momencie, w którym zostanie osiągnięty znak odcięcia (!), wszystkie podcele B 1,B 2,...,B n są już spełnione. Po przekroczeniu znaku odcięcia (!) rozwiązania tych podcelów zostają zamrożone (w szczególności ukonkretnione zmienne zachowują nadane im wartości), zaś alternatywne sposoby ich spełnienia nie będą analizowane.
Predykat fail Predykat! (cut - predykat odcięcia) interpretujemy logicznie jako zawsze prawdziwy. Jak wiemy już predykat ten służy do ograniczania nawrotów. Predykat fail interpretujemy logicznie jako zawsze fałszywy. Predykat ten służy do wymuszania nawrotów. Połączenie!,fail interpretujemy logicznie jako negację.
Predykat fail Rozpatrzmy teraz bazę danych: ojciec(filip,szymon). ojciec(filip,marek). dziecko(kasia,szymon). dziecko(julia,szymon). dziecko(joasia,marek). rodzic(x,z):- ojciec(x,y). Wówczas:?- rodzic(filip,x). X = szymon ; X = marek.
Predykat fail Rozpatrzmy teraz bazę danych: ojciec(filip,szymon). ojciec(filip,marek). dziecko(kasia,szymon). dziecko(julia,szymon). dziecko(joasia,marek). rodzic(x,y):- ojciec(x,y),write(x),nl,write(y),nl,fail. Wówczas:?- rodzic(filip,x). filip szymon filip marek
Predykat fail Rozpatrzmy teraz bazę danych: ojciec(filip,szymon). ojciec(filip,marek). dziecko(kasia,szymon). dziecko(julia,szymon). dziecko(joasia,marek). rodzic(x,y):- fail,ojciec(x,y),write(x),nl,write(y),nl. Wówczas:?- rodzic(filip,x).
Drzewa Rozważmy następujące drzewo: a b c d e f g h i j k l Chcemy zapisać powyższe drzewo w prologu + zdefiniować pewne predykaty związane z przetwarzaniem drzew.
Drzewa Drzewo możemy zapisać następująco: rodzic(a,b). rodzic(a,c). rodzic(a,d). rodzic(b,e). rodzic(b,f). rodzic(c,g). rodzic(c,h). rodzic(c,i). rodzic(d,j). rodzic(f,k). rodzic(f,l). e k a b c d f g h i j l
Drzewa Definiujemy predykat: rodzenstwo rodzenstwo(a,b):-rodzic(c,a),rodzic(c,b), A\==B. Przykład:?- rodzenstwo(a,b).?- rodzenstwo(b,c). true ;?- rodzenstwo(b,x). X = c ; X = d ; e k a b c d f g h i j l
Drzewa Definiujemy predykat: ten_sam_poziom ten_sam_poziom(x,x). ten_sam_poziom(x,y):-rodzic(a,x), rodzic(b,y), ten_sam_poziom(a,b). Przykład:?- ten_sam_poziom(a,b).?- ten_sam_poziom(c,b). true ; e a b c d f g h i j k l
Drzewa Definiujemy predykat: poziom poziom(a,0). poziom(x,n):-rodzic(y,x), poziom(y,m),n is M+1. Przykład: a?- poziom(a,x). X = 0 ;?- poziom(k,x). X = 3 ; e k b c d f g h i j l
Drzewa Definiujemy predykat: path path(a). path(x):-rodzic(a,x),path(a), write(a),write( -> ). Przykład:?- path(b). a-> true ; 14?- path(k). a->b->f-> true e k a b c d f g h i j l
Drzewa Definiujemy predykat: znajdz Przykład: znajdz(x):-path(x),write(x).?- znajdz(b). a->b true ;?- znajdz(k). a->b->f->k true ; e k a b c d f g h i j l
Drzewa Definiujemy predykat: lisc Przykład: lisc(x):-not(rodzic(x,_)). a?- lisc(b).?- lisc(k). true. e b c d f g h i j k l
Drzewa binarne Rozważmy następujące drzewo binarne: a b d e f i Chcemy zapisać powyższe drzewo w prologu + zdefiniować predykaty związane z przeszukiwaniem tego drzewa.
Drzewa binarne Wykorzystamy predykaty: emptybt - puste drzewo consbt(n,t1,t2)- drzewo binarne z korzeniem N, lewym poddrzewem T1 i prawym T2. Przykład: a consbt(a,consbt(b,emptybt,emptybt), consbt(d,emptybt,emptybt)). b d
Drzewa binarne Przykład: a b d e f i consbt(a,consbt(b,consbt(e,emptybt,emptybt), consbt(f,emptybt,emptybt)), consbt(d,consbt(i,emptybt,emptybt),emptybt)).
Drzewa binarne Chcemy zdefiniować predykat: preorder(t,l) oznaczający, że L jest listą wierzchołków drzewa binarnego uzyskaną w wyniku przeszukiwania predorder. Predykat definiujemy następująco: preorder(emptybt,[]). preorder(consbt(n,t1,t2),l):- preorder(t1,l1), preorder(t2,l2), sklej([n L1],L2,L).
Drzewa binarne Przykład: a b c?-preorder(consbt(a,consbt(b,emptybt, emptybt),consbt(c,emptybt,emptybt)),x). X = [a, b, c].
Drzewa binarne Przykład: a b d e f i?- preorder(consbt(a,consbt(b,consbt(e,emptybt, emptybt),consbt(f,emptybt,emptybt)),consbt(d, consbt(i,emptybt,emptybt),emptybt)),x). X = [a, b, e, f, d, i].
Czytanie i pisanie znaków Znak jest najmniejszą porcją danych, którą możemy odczytać. W prologu znakami są atomy, które w nazwie mają dokładnie jeden element. Znak można odczytać za pomocą predykatu: get_char(x). przykład:?- get_char(x). : 7 X = '7'.?- get_char(x). : a X = a.?- get_char(x). : ala X = a.
Czytanie i pisanie znaków W przypadku gdy zmienna X jest ukonkretniona znakiem zostanie on wypisany na ekranie po wywołaniu: przykład: put_char(x).?-x='a',put_char(x). a X = a.?- put_char(x). ERROR: put_char/1: Arguments are not sufficiently instantiated
Czytanie i pisanie znaków przykład: p:- get_char(z),write(z),p. wówczas:?- p. : abc abc : (rekurencja bez stopu!)
Czytanie i pisanie znaków przykład: p:-get_char(z),write('znak:'), write(z),nl,\+(z=k),p. wówczas:?-?- p. : abc znak: a znak: b znak: c znak: : k znak: k
Koniec