Struktury danych i obliczenia Przemysław Podkowiński, Michał Przybylski Arytmetyka Przykłady jak wygląda prosta arytmetyka w Prologu: ?- Y is 2+2. Y=4 ?- 5 is 3+3. false ?- Z is 4.5 + (3.9 / 2.1). Z = 6.3571428 Predykat „is” oblicza wyrażenie arytmetyczne z prawej strony i przypisuje je do wyrażenia po lewej stronie. Operatory • • • • • • + * / // mod dodawanie odejmowanie mnożenie dzielenie zmiennoprzecinkowe dzielenie całkowite modulo Funkcje • abs() - wartość bezwzględna • sqrt() – pierwiastek kwadratowy • log() – logarytm, przy podstawie e • exp()- funkcja wykładnicza, podstawa e • floor() – największa wartość integer mniejsza lub równa argumentowi • round()- zaokrąglenie do najbliższej liczby Operatory • R is wyrażenie – ocenia i przypisuje wartość • wyrażenie1 =:= wyrażenie2 – jeżeli równe • wyrażenie1 =\= wyrażenie2 – jeżeli różne • wyrażenie1 > wyrażenie2 – jeżeli większe • wyrażenie1 < wyrażenie2 – jeżeli mniejsze • wyrażenie1 >= wyrażenie2 – jeżeli większe bądź równe • wyrażenie1 =< wyrażenie2 – jeżeli mniejsze bądź równe Konstruowanie wyrażeń • ?- 4+1 =:= 2+3. true. • ?- 4 is sqrt(16). false. • ?- 4.0 is sqrt(16). true. • ?- What is 2 + 3*4 + 5. What = 19. Praktyczne przykłady • Często zostajemy zmyleni przez błędne zaokrąglenie, dlatego zdefiniujmy predykat close_enough który pozwoli nam porównać liczby zmiennoprzecinkowe. close_enough(X,X) :- !. close_enough(X,Y) :- X<Y, Y-X<0.0001. close_enough(X,Y) :- X>Y, close_enough(Y,X). close_enough(X,X) :- !. – jest to przypadek gdy dwa argumenty są równe. close_enough(X,Y) :- X<Y, Y-X<0.0001. - porównuje X i Y, odejmuje mniejszą od większej i sprawdza róznice, jeśli jest mniejsza od 0.0001. close_enough(X,Y) :- X>Y, close_enough(Y,X). – porównuje X i Y, jeśli X jest większe od Y to wywołuje sama siebie ponownie tylko z zamienionymi parametrami. Praktyczne przykłady • Obliczmy pierwiastek kwadratowy z danej liczby real_square_root(X,nonexistent) :- X < 0.0. real_square_root(X,Y) :- X >= 0.0, Y is sqrt(X). Przykład: ?- real_square_root(9.0,Root). Root =3.0 ?- real_square_root(-1.0,Root). Root =nonexistent Zapytanie real_square_root(121.0,11.0) daje true, ale real_square_root(121.0,11.0) już daje false, ponieważ 11.0 nie równa się dokładnie zmienno-przecinkowem wynikowi sqrt pomimo, że pierwiastek ze 121 wynosi dokładnie 11. • Zmodyfikujmy naszą definicję tak by zwracała oba pierwiastki kwadratowe a nie jeden. Do tego potrzebujemy jeszcze jednej alternatywy: real_square_root(X,Y) :- X > 0.0, R is -sqrt(X). Co daję nam w sumie taki oto predykat: real_square_root(X,nonexistent) :- X < 0.0. real_square_root(X,Y) :- X >= 0.0, Y is sqrt(X). real_square_root(X,Y) :- X > 0.0, Y is -sqrt(X). Przykład: ?- real_square_root(9.0,Root). Root =3.0 Root =-3.0 Ciągi znaków Są trzy sposoby na zaprezentowanie ciągu znaków w Prologu: • Jako atom. • Jako lista kodów ASCII. • Jako lista jedno znakowych atomów. Jeśli wpiszemy („like this”) to komputer zinterpretuje to jako liste kodów ASCII. A więc „abc” i [97,98,99] dla Prologa to dokładnie to samo. Lista kodów ASCII to tradycyjnie nazywany string. Wprowadzanie linii jako string albo atom • Łatwo zrobić w Prologu czytanie całej linii do pojedynczego stringa, bez używania read, a zamiast tego użycie get 0. Oto sposób jak to zrobić: % read_str(String) % Accepts a whole line of input as a string (list of ASCII codes). % Assumes that the keyboard is buffered. read_str(String) :- get0(Char), read_str_aux(Char,String). read_str_aux(-1,[]) :- !. % end of file read_str_aux(10,[]) :- !. % end of line (UNIX) read_str_aux(13,[]) :- !. % end of line (DOS) read_str_aux(Char,[Char|Rest]) :- read_str(Rest). • Jeśli chcemy przeczytać linie nie jako string ale jako atom. Jest to proste, ponieważ Prolog ma wbudowany predykat name/2: ?- name(abc,What). What = [97,98,99] % equivalent to "abc" ?- name(What,"abc"). What = abc ?- name(What,"Hello there"). What = 'Hello there' yes ?- name(What,[97,98]). What = ab Struktury • Wiele warunków w Prologu składa się z funktora wynikającego z zera albo większej ilości warunków takich jak: a(b,c) alpha([beta,gamma],X) 'this and'(that) f(g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v) i_have_no_arguments Warunki tej formy nazywane są Structures(Struktury). Funktor zawsze jest atomem, ale argumenty mogą być warunkami każdego typu. Struktura bez argumentów jest po prostu atomem. • Przykład użycia struktur: person(name('Michael Covington'), gender(male), birthplace(city('Valdosta'), state('Georgia'))) sentence(noun_phrase(determiner(the), noun(cat)), verb_phrase(verb(chased), noun_phrase(determiner(the) ), noun(dog)))) • Struktury działają podobnie jak listy, ale inaczej są przechowywane w pamięci. • Jedną z ważniejszych różnic to to, że lista jest podzielona na head i tail, a struktura nie jest. Struktura zjednoczy się z inna kiedy ma taki sam funktor i taka samą ilość argumentów. Unify With Result a(b,c) X X=a(b,c) a(b,c) a(X,Y) X=b, Y=c a(b,c) a(X) fails a(b,c) a(X,Y,Z) fails „Occurs check” • Możemy stworzyć dziwaczna, zapętloną strukturę. • Struktury te zawierają wskaźniki do siebie, a one doprowadzają do pętli bez końca. ?- X = f(X). X = f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f... ?- X = [a,b,X] X = [a,b,[a,b,[a,b,[a,b,[a,b,[a,b,[a,b,[a,b[a,b,[a,b[a,b,[a,b... ?- f(X) = f(f(X)) X = f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(... Standard ISO posiada funkcje unify_with_occurs_check która odpowiada za kontrole przed próba wykonania: ?- unify_with_occurs_check(X,f(X)). no. ?- unify_with_occurs_check(X,f(a)). X = f(a) Konstruowanie celów na starcie • Ponieważ zapytanie w Prologu jest strukturą, możesz je traktować jako dane i konstruować je podczas uruchomienia programu. Call wykonuje argument jak pytanie, więc call(write(`hello there`)) jest równoznaczne z write(`hello there`) Cel może być stworzony przez obliczenie. Dla przykładu: answer_question :- write('Mother or father? '), read_atom(X), write('Of whom? '), read_atom(Y), Q =.. [X,Who,Y], call(Q), write(Who), nl. Jak wpiszemy mother i cathy wtedy Q będzie wyglądało tak: mother(Who, cathy). ?- answer_question. Mother or father? father Of whom? michael charles_gordon yes ?- answer_question. Mother or father? mother Of whom? Melody eleanor yes Strategie przechowywania danych Są trzy sposoby na przechowywanie danych w programach w Prologu: • W instancji zmiennej. Nie jest to trwały sposób przechowywania danych, ponieważ działa tylko w zasięgu klauzuli która je definiuje. • W argumentach. Lista argumentów jest sposobem by procedura komunikowała się. Procedura może wykonać rekurencyjnie proces i zapisywać informacje z jednej rekurencji do drugiej. • W bazie wiedzy. Jest to najtrwalszy sposób przechowywania danych. Przykłady: count(X) :- retract(count_aux(N)), X is N+1, asserta(count_aux(X)). :- dynamic(count_aux/1). count_aux(0). ?- count(X). X=1 yes ?- count(X). X=2 Yes