Prolog * bli*sze spojrzenie

advertisement
Prolog – bliższe spojrzenie
Michał Wujkowski
Przemysław Cecelski
Kamil Rychliński
Mateusz Iwański
Listy
Listy w Prologu mogą przechowywać dane dowolnego typu
 [alpha,beta,gamma,delta]
 [1,2,3]
Sama lista również może zawierać listę:
 [[a,list,within],a,list]
Listy mogą być tworzone przez zjednoczenie , cała list zostaje
przypisane do danej zmiennej lub kilku zmiennych, dotyczy to
również list lub struktur w nich osadzonych(przykład 3,4) np.:
Unify
With
Result
[a,b,c]
X
X=[a,b,c]
[X,Y,Z]
[a,b,c]
[[a,b],c]
[X,Y]
[a(b),c(X)]
[Z,c(a)]
X=a, Y=b,
Z=c
X=[a,b] ,
Y=c
X=a, Z=a(b)
Każda lista może być podzielona na głowę i ogon za pomocą
symbolu „ | „ . Oczywiście głowa to pierwszy element, ogon
pozostałe.
Ogon w odróżnieniu od głowy może być pusty.
 [a|[b,c,d]] = [a,b,c,d]
 [a|[]] = [a]
 [ ] - oznacza listę pustą
Unify
With
Result
[X|Y]
[a,b,c,d]
X=a, Y=[b,c,d]
[X|Y]
[a]
X=a, Y=[]
Najciekawsze przykłady z wieloma elementami w head/tail , nie
zawsze jest to jeden element! np.:[a,b,c|[d,e,f]] = [a,b,c,d,e,f]
Unify
With
Result
[X,Y|Z]
[a,b,c]
X=a, Y=b, Z=[c]
[X,Y|Z]
[a,b,c,d]
[X,Y,Z|A]
[a,b,c]
[X,Y,Z|A]
[a,b]
X=a, Y=b,
Z=[c,d]
X=a, Y=b, Z=c,
A=[]
fails
Przykład
Program:
third_element([A,B,C|Rest],C). – definicja listy
Wywołanie:
1) third_element([a,b,c,d,e,f],X).
2) third_element([a,b,Y,d,e,f],c).
Wynik:
1) X = c
2) Y = c
Błąd podczas kompilacji:
 Singleton variables: [A,B,Rest]
Powyższy błąd jest spowodowany tym, iż mamy program w
postaci:
third_element([A,B,C|Rest],C).
Gdzie elementy A,B,Rest nie są nigdzie używane i nie posiadają
wartości, jeżeli takich zmiennych nie potrzebujemy możemy
zastąpić je znakiem _ wtedy wspomniany błąd znika tj.:
third_element([_,_,C|_],C).
 Zadanie, sprawdzić czy pierwsze dwa elementy są jednakowe:
 Program:
takie_same([A,A,_|_]).
 Wywałania:
1) takie_same([a,b,c,d,e,f]).
2) takie_same([a,a,c,d,e,f]).
3) takie_same([a,X,c,d,e,f]).
Wyniki:
1) false
2) true
3) X=a
W 3. przypadku do zmiennej X przypisana została wartość
pierwszego elementu
 Zadanie, zamienić pierwsze dwa elementy listy i zwrócić
pozostałe
 Program
takie_same([A,B|Z],[B,A|Z]).
 Wywołanie
1) takie_same([x,i,c,d],K)
2) takie_same([x,[1,2,3],c,d],K).
 Wy n i k
1 ) K = [i, x, c, d]
2 ) K = [[1, 2, 3], x, c, d]
Listy jako rekordy
Lista może przechowywać dane w postaci rekordów, dla
przykładu przechowajmy dane osobowe:
['Michael Covington',
'285 Saint George Drive',
'Athens',
'Georgia',
'30606']
Najważniejszą różnicą jest fakt, że liczba elementów listy nie
musi być z góry określona a elementy list nie muszą być danego
typu co daje możliwość zapisanie np. daty urodzenie i
jednocześnie nazwiska.
Lista może zawierać inną listę jak wcześniej wspomnieliśmy dla
przykładu nazwisko osoby, dzieci, szkoła:
[ `Adam Nowak `,
[`Marta`,
`Maciek`,
`Ada`],
`Uniwersytet Łódzki`]
Listy można użyć również jako tablicy np. do przechowania
macierzy:
[[1,2,3],
[4,5,6],
[7,8,9]]
Należy pamiętać jednak, że czas dostępu do elementu na liście
jest zależny od odległości elementu od początku listy, ponieważ
komputer musi przejrzeć całą listę od początku aby znaleźć
potrzebny nam element w przypadku tablicy czas dostępu do
danej jest zawsze taki sam.
Rekurencja a listy
Nie zawsze możemy jasno określić ile elementów będzie miała
nasza lista wtedy z pomocą przychodzi nam rekurencja.
Weźmy pod uwagę takie zadanie, mamy sprawdzić czy X jest
elementem listy Y, oczywiście nie wiemy z góry ile elementów
ma lista Y, nie możemy w tym przypadku użyć skończonej liczby
predeterminowanych pozycji, musimy przeszukiwać tak długo
listę, aż nie znajdziemy elementu X lub nie skończą się dane do
przeszukiwania – lista Y.
Należy również obsłużyć przypadek w którym list Y będzie
pusta.
Program:
member(X,[X|_])
member(X,[_|Ytail]) :- member(X,Ytail)
Wywołanie:
1) member(c,[a,b,c]). - true
2) member(f,[a,b,c]). – false
Schemat nie pasuje do klauzuli 1 więc program kontynuuje
działanie w drugim „przebiegu” wywołane jest
member(c,[b,c]). Idąc dalej otrzymamy
member(c,[c]) w tym przypadku [c]=[c|[]] tak więc
otrzymamy wartość true.
Przeliczanie elementów listy
Zakładamy że lista jest pusta
List_length([],0).
W innym przypadku pomiń pierwszy element i policz liczbę
pozostałych w Tail, algorytm działa również rekurencyjnie,
ponieważ żeby policzyć całość początkowo liczy elementy mniejszej
listy.
Kod:
list_length([],0)
list_length([_|Tail],K) :list_length(Tail,J), K is J + 1.
?- list_length([a,b,c],K0).
?- list_length([b,c],K1).
?- list_length([c],K2).
?-listl_ength([],0).
?- K2 is 0+1.
?- K1 is 1+1.
?- K0 is 2+1.
Łączenie list
Do łączenie list nie możemy użyć znaku „|” , wynikiem nadal byłaby lista w
liście dla przykładu weźmy [a,b,c] z [d,e,f], aby uzyskać
[a,b,c,d,e,f]
W momencie kiedy użyjemy „|” otrzymamy następujący wynik:
[[a,b,c],d,e,f]
Na początek zajmijmy się warunkiem ograniczającym, gdyż lista
ewentualnie zostanie pusta.
append([],X,X).
Rekurencyjna klauzula :
append([X1|X2],Y,[X1|Z]) :- append(X2,Y,Z).
Weź pierwszy element pierwszej listy(nazwij X1) Rekurencyjnie dołącz
ogon pierwszej listy do całej drugiej listy. Nazwij rezultat Z. Dodaj X1 do
początku Z.
Rekurencyjne odwracanie listy
Podziel oryginalną listę na head(głowę) i tail(ogon).
2. Rekurencyjnie odwrócić tail(ogon) oryginalnej listy.
3. Stworzyć liste której jedyne elementy są head(głową)
oryginalnej listy.
4. Powiązać odwrócony tail(ogon) oryginalnej listy z listą
stworzoną w kroku 3.
1.
reverse([],[]).
reverse([Head|Tail], Result) :reverse(Tail,ReversedTail),
append(ReversedTail,[Head],Result).
Ciągi znaków
W Prologu można reprezentować ciagi znaków na 3 sposoby:
- Jako atom. Atomy są kompaktowe ale ciężko nimi
manipulować
- Jako lista kodów ASCII
- Jako lista jednoznakowych atomów.
Wpisując słowo „abc”, Prolog interpretuje to jako następujące
kody ASCII [97,98,99].
Teoria z książki mówi, że problemem jest wyświetlenia samych
znaków ponieważ przy użycia funkcji write lub display
otrzymamy liste numerów ASCII zamiast napisu.
Nie zauważyliśmy tego, ponieważ kompilator w naszej wersji
zwraca właściwy już napis.
Zakładając jednak, że jest inaczej należy użyć listy w następujący
sposób:
write_str([Head|Tail]) :- put(Head),
write_str(Tail).
write_str([]).
-- instrukcja put zamienia kod ASCII na literę
Rekurencja jest prosta do śledzenia. Jeżeli string nie jest pusty (tak
więc będzie się zgadzał [Head|Tail]) wypisze pierwszą pozycjie i
powtórz procedure dla pozostałych pozycji. Kiedy String stanie się
pusty, zakończ powodzeniem bez kolejnych akcji. Stringi są listami w
każdym sensie słowa i kazda lista technik przetwarzania może być na
nich użyta. Tak więc reverse odwróci string-a, append zwiąże lub
podzieli string itd..
Struktury danych i obliczenia
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
- 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
 abs()





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.
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.
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)
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.
Zadanie
 Napisz funkcję w prologu, która odwraca listę list,
 Tzn:
 reverseLL([[1,2,3],[4,5],[8,9]],X)
 X = [[3,2,1],[5,4],[9,8]]
 reverseLL([],[])
 reverseLL([X|Y],Z) :- reverse(X,A), reverseLL(Y,B),
append(A,B,Z).
Download