Wykonali: Jakub Gutkowski, Klaudia Belka, Damian Koncewicz Using data Structures Wykonali: Jakub Gutkowski, Klaudia Belka, Damian Koncewicz
Struktura drzewa Łatwiej jest nam zrozumieć budowę struktury, jeśli zapiszemy ją w postaci drzewa, w którym korzeń jest funktorem, a gałęziami są elementy struktury. Przykłady: Struktura parents(charles, elizabeth, philip)
2. Struktura a+b. c lub równowążnie +(a,. (b,c)) 3 2. Struktura a+b*c lub równowążnie +(a,*(b,c)) 3. Struktura book(moby_dick, author(herman, melville))
Drzewa również mogą dać nam graficzny opis zmiennych wewnątrz struktur, szczególnie pokazując jak współużytkowane są zmienne o podobnych nazwach. Przykład: f(X, g(X, a))
Listy – powszechna struktura danych Lista jest uporządkowanym ciągiem elementów, rozdzielonych przecinkami i umieszczonych między kwadratowymi nawiasami, które mogą mieć dowolną długość. Elementami listy mogą być dowolne termy(stałe, zmienne, struktury). [a,b,c] [a,123,[],[a,3],B] [] – Lista także może być pusta
Prolog przetwarzając listy dzieli je na dwie części – głowę i ogon Prolog przetwarzając listy dzieli je na dwie części – głowę i ogon. Głową jest zawsze pierwszy element listy, a ogonem pozostała reszta. Gdy chcemy, by prolog podzielił nam listę, używamy symbolu pionowej kreski | i wpisujemy nasze zapytanie po znaku zachęty (?-) w oknie głównym: [LISTA] = [G|O]. , gdzie G to głowa, a o to ogon. Przykłady 1.
Lista zawierająca pojedynczy element ‘a’: .(a, []) 1. Lista zawierająca pojedynczy element ‘a’: .(a, []) 2. Lista składająca się z atomów a, b, c: .(a,.(b,.(c,[])))
Listy mogą być tworzone przez zjednoczenie, cała lista zostaje przypisana do danej zmiennej lub kilku zmiennych. Dotyczy to również struktur bądź list w nich osadzonych.
Predykat Jest zbiorem, będącym podzbiorem iloczynu kartezjańskiego zbiorów związanych z tym predykatem. W Prologu dostępnych jest wiele predykatów wyrażających warunki na listach. Najważniejsze z nich to: member(X,L) spełniony, gdy X jest elementem listy L append(L1 ,L2 ,L3) spełniony, gdy lista L3 jest połączeniem list L1 i L2 select(X, L1, L2) spełniony, gdy lista L2 jest listą pozostałą z listy L1 po wyjęciu z niej elementu x. Przykłady 2.
Przeszukiwanie rekurencyjne Często musimy przeszukiwać struktury w Prologu w celu znalezienia pewnych informacji. Gdy struktury mogą mieć inne struktury jako argumenty, konieczne jest przeszukiwanie rekurencyjne. Przykłady 3.
member(X,[X|_]).
member(X,[_|O]):-member(X,O).
member(X,[X|_]). member(X,[_|O]):-member(X,O).
Mapping Tzw. Odwzorowanie, czyli przechodzenie przez kolejne składniki starej struktury jednocześnie tworząc nowe struktury. Przykład: You are a computer. I am not a computer. Aby wykonać odwzorowanie należy: Wczytać zdanie podane przez użytkownika Zmienić każdą formę 2 os. na 1 os. Zmienić You na I am not
Przykład: Dla listy [you, are, a, computer] change(you, i). change(are, [am,not]). change(X, X). alter([], []). alter([Z|T], [X|Y]):- change(Z,X), alter(T,Y). Zapytanie: alter([you, are, a, computer],R).
Porównanie rekurencyjne W Prologu możemy porównywać liczby całkowite, możemy również porównywać struktury co jest bardziej złożone, ponieważ trzeba uwzględnić każdy poszczególny komponent. Gdy składniki są strukturami, porównanie może być rekurencyjne. Dzieje się tak gdy chcemy porównać np. listy o dowolnej długości. Pokażę, to na przykładzie zużycia paliwa przez samochody na różnych drogach. Oczywiście aby miało to sens trzeba porównać spalanie na tych samych drogach.
Porównanie rekurencyjne fuel_consumed(waster, [3.1, 10.4, 15.9, 10.3]). fuel_consumed(guzzler, [3.2, 9.9, 13.0, 11.6]). fuel_consumed(prodigal, [2.8, 9.8, 13.1, 10.4]). equal_or_better_consumption(Good, Bad):- Threshold is (Good + Bad) /40, Worst is Bad + Threshold, Good < Worst.
Łączenie list Lista predykatów przetwarzania list służy do łączenia dwóch list razem w celu utworzenia kolejnej, nowej listy. Przykład: append([a,b,c], [3,2,1], [a,b,c,3,2,1]). Oznacza to, że pierwsza tablica i druga w połączeniu daje nam 3 tablice. Możemy to również użyć, aby sprawdzić jakiego wyrażenia nie ma w pierwszej tablicy: append(X,[a,b,d],[a,b,c,d]).
Łączenie list Lista predykatów przetwarzania list służy do łączenia dwóch list razem w celu utworzenia kolejnej, nowej listy. Przykład: append([a,b,c], [3,2,1], [a,b,c,3,2,1]). Oznacza to, że pierwsza tablica i druga w połączeniu daje nam 3 tablice. Możemy to również użyć, aby sprawdzić jakiego wyrażenia nie ma w pierwszej tablicy: append(X,[a,b,d],[a,b,c,d]).
Akumulatory Jest to pewnego rodzaju metoda programowania w Prologu polegająca na użyciu dodatkowego argumentu predykatu, w którym możemy zapisać wynik pośredni do dalszych działań.
Wyjaśnienie działania Przykładem zastosowania akumulatorów jest odwracanie listy [a, b, c, d]. Na początku nasz akumulator będzie pusty. Bierzemy głowę listy, którą próbujemy odwrócić, i dodajemy ją jako akumulator. Następnie przetwarzamy ogon, w związku z czym mamy do czynienia z odwróceniem [b, c, d], a naszym akumulatorem jest [a]. Ponownie bierzemy głowę listy, którą próbujemy odwrócić i dodajemy ją jako głowę akumulatora (stąd nasz nowy akumulator to [b, a]). Kontynuujemy próbę odwrócenia [c, d]. Ponownie robimy to samo, więc otrzymujemy nowy akumulator [c, b, a] i odwracamy ostatni element [d]. Otrzymujemy [d, c, b, a] i nowy cel próby odwrócenia []. Tutaj proces się zatrzymuje, a nasz akumulator zawiera odwróconą listę, którą chcieliśmy otrzymać. Podsumowując: chodzi o to, aby przejść przez listę, którą chcemy odwrócić, i przesunąć każdy element z kolei na czoło akumulatora. Po ostatniej wykonanej akcji otrzymamy odwróconą listę.
Przykład Odwrócenie tablicy
Generowanie wielu rozwiązań
Odcięcie / Nawracanie Mechanizm odcięcia pozwala zablokować nawracanie przez Prolog do wcześniej dokonanych wyborów. Zajmijmy się na początku wieloma rozwiązaniami. Mamy bazę z następującymi faktami:
ojciec(maria,jerzy). ojciec(jan,jerzy). ojciec(patryk,jerzy). ojciec(zuzanna,henryk). ojciec(marysia,edward).
Zadając pytanie: ?-ojciec(X,Y). Otrzymamy: X = maria, Y = jerzy ; X = jan, Y = jerzy ; X = patryk, Y = jerzy ; X = zuzanna, Y = henryk ; X = marysia, Y = edward. Dzięki nawracaniu znajdowane są wszystkie rozwiązania z bazy danych. Prolog w żadnym przypadku nie analizuje czy też nie zapamiętuje zwracanych wyników Jak widać odpowiedź Y=jerzy; pojawiła się trzykrotnie, ale z punktu widzenia języka są to dwa różne rozwiązania.
Rozważmy teraz przykład z dwoma celami mającymi po kilka rozwiązań a(X,Y) :- b(X), c(Y). b(1). b(2). b(3). c(4). c(5). c(6).
Aby kontrolować sposób realizacji programu w Prologu używamy operatora odcięcia „ ! „ Jeśli prolog natrafi na operator odcięcia w regule, nie będzie nawracał z wyborem do wcześnieszych możliwości.
Prolog próbuje udzielić odpowiedzi na zapytanie a(X,Y) Prolog próbuje udzielić odpowiedzi na zapytanie a(X,Y). Najpierw korzysta z pierwszej lini programu. Aby udzielić odpowiedzi najpierw bada b(X). Co w wyniku do X przypisuje wartość 1 Następnie natrafia na odcięcie co powoduje że ustala X = 1 jako jedyne rozwiązanie dla X
Typowe zastosowania odcięcia Przypadki kiedy chcemy poinformować system, że dla danego celu już została znaleziona odpowiednia reguła. Sytuacje kiedy chcemy poinformować Prologa, że dany cel ma natychmiast zawieść bez sprawdzania rozwiązań alternatywnych Przypadki kiedy należy zaprzestać generowania rozwiązań alternatywnych przez nawracanie.
Bibliografia: Programming in Prolog – William F. Clocksin, Christopher S. Mellish Programowanie w logice Prolog – Piotr Fulmański http://merlin.phys.uni.lodz.pl/MSkulimowski/prolog/Prolog_3_2017. pdf http://kuba0x21.pl/akumulatory/