Intro
-
Upload
api-26356906 -
Category
Documents
-
view
184 -
download
3
Transcript of Intro
Szybkie wprowadzenie do Prologu
Roman Huk
31 października 2007
Streszczenie
Celem tego artykułu jest przedstawienie w skrótowej formie (przy-znaję — nie zawsze wyczerpującej — ale od czego są podręczniki?)podstawowych zasad pisania programów w języku Prolog. W zamie-rzeniu jest to pierwszy z serii artykułów na ten temat. W tym „od-cinku” zostały zaprezentowane podstawowe informacje, pozwalające naopanowanie Prologu na tyle, żeby móc samodzielnie eksperymentowaćz programowaniem w logice.
Spis treści
1 Wprowadzenie 11.1 Notacja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.2 Dopasowywanie . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2 Pierwsze przykłady 42.1 Co kto lubi? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.2 Struktury danych . . . . . . . . . . . . . . . . . . . . . . . . . 52.3 Arytmetyka . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3 Listy 73.1 Lista i jej elementy . . . . . . . . . . . . . . . . . . . . . . . . 73.2 Operacje na listach . . . . . . . . . . . . . . . . . . . . . . . . 83.3 Jeszcze o listach . . . . . . . . . . . . . . . . . . . . . . . . . 10
4 Grafy 114.1 Ścieżki w grafie nieskierowanym . . . . . . . . . . . . . . . . . 114.2 Graf skierowany . . . . . . . . . . . . . . . . . . . . . . . . . . 124.3 Drzewo (genealogiczne) . . . . . . . . . . . . . . . . . . . . . 12
5 Prosty system ekspertowy 12
1
1 Wprowadzenie
Czy naukę języka programowania należy prowadzić w sposób bardzo usys-tematyzowany? Zapewne w wielu przypadkach jest to wskazane — a nastudiach technicznych wręcz pożądane. Mógłbym również tak zrobić w przy-padku Prologu i zacząć zajęcia od słów „Podstawowymi elementami językasą termy, zmienne i predykaty. Termem nazywamy itd.”. Jednak czy w tymprzypadku jest to konieczne? Po pierwsze, jest to kolejny język programo-wania, który poznają słuchacze — to prawda, że inny od języków impera-tywnych, ale każdy we własnym zakresie będzie mógł stwierdzić, że „Pod-stawowymi elementami języka...itd.”; po drugie, w tym przypadku naukajęzyka nie jest celem samym w sobie — Prolog ma być jedynie narzędziempomocnym w realizacji ćwiczeń z przedmiotu „Sztuczna Inteligencja”. Nieo samo opanowanie języka zatem chodzi, lecz bardziej o to, jak (przy jegopomocy) rozwiązać niektóre problemy, trudno rozwiązywalne „klasycznymi”metodami.
Takie podejście doskonale koresponduje z filozofią Prologu, która mówi,że program ma określać „co trzeba zrobić” a nie „jak trzeba zrobić”. Dlategowybrałem formę prezentacji konstrukcji języka by-example. Zatem do dzieła!
1.1 Notacja
Najważniejszym źródłem informacji o Prologu jest system pomocy, dostar-czany z każdym interpreterem/kompilatorem Prologu. Oto fragment infor-macji, pochodzący z mojego środowiska (GNU Prolog):
|7.20.2 member/2,membercheck/2|Templates| member(?term,?list)| membercheck(?term,?list)|Description| member(Element, List) succeds if Element belongs to list.| This predicate is| . . .
Co oznaczają te dziwne zapisy? Otóż w punkcie 7.20.7 systemu pomocyopisane jest działanie predykatów member i membercheck, które wymagajądwóch argumentów. Pierwszym z nich jest term, drugim lista. znak ’?’poprzedzający argumenty mówi o tym, że argument może być ukonkretnioną(instantiated) wartością lub nieukonkretnioną zmienną. Czasami pojawiająsię inne znaki: znak ’+’ oznacza, że argument musi mieć ukonkretnionąwartość, zaś znak ’–’ oznacza, że argument musi być zmienną (która zostanieukonkretniona jakąś wartością jeżeli predykat powiedzie się).
2
1.2 Dopasowywanie
Prolog wykorzystuje dopasowywanie do wzorców, zwane unifikacją. Jestto niezwykle elastyczne dopasowywanie. Do tego celu używa się predykatu=/2. Najprostsze zapytania to zapytania o termy:
| ?- a=a.
yes| ?- a=b.
no
Termy mogą być bardziej złożone, przetwarzanie również powiedzie się, gdytermy są identyczne lub zawiedzie:
| ?- f(a(b))=f(a(b)).
yes| ?- f(a(b))=f(b(a)).
no
Można wprowadzić zmienną — zarówno z lewej jak i z prawej strony znakurówności:
| ?- X=a.
X = a
yes| ?- b=Y.
Y = b
yes
Unifikacja zapewnia, że zmienna zostanie zwrócona ”wypełniona treścią”,ukonkretniona, jeżeli przetwarzanie powiedzie się. Popatrzmy na bardziejskomplikowane struktury:
| ?- f(a)=X.
X = f(a)
yes| ?- f(a,B,c,D)=f(A,b,C,d).
3
A = aB = bC = cD = d
yes
2 Pierwsze przykłady
2.1 Co kto lubi?
Stwórzmy pierwszy miniprogram:
| ?- consult(user).compiling user for byte code...lubi(jan,herbata).lubi(jan,piwo).lubi(tomasz,piwo).lubi(tomasz,kawa).lubi(adam,kawa).
user compiled, 6 lines read - 606 bytes written, 65902 ms
(12 ms) yes| ?-
Oto kilka prostych pytań:
| ?- lubi(jan,piwo).
yes| ?- lubi(jan,X).
X = herbata ? ;
X = piwo
yes| ?- lubi(X,piwo).
X = jan ? ;
X = tomasz ? ;
no
4
Sspróbujmy znaleźć dwóch piwoszy w jednym zapytaniu:
| ?- lubi(X,piwo),lubi(Y,piwo).
X = janY = jan ? ;
X = janY = tomasz ? ;
X = tomaszY = jan ? ;
X = tomaszY = tomasz ? ;
no
Czy nie przypomina to iloczynu kartezjańskiego? zatem trzeba nałożyć do-datkowe warunki:
| ?- lubi(X,piwo),lubi(Y,piwo),X \= Y.
X = janY = tomasz ? ;
X = tomaszY = jan ? ;
no
Już lepiej, ale to w dalszym ciągu nadmiar informacji. Na razie jednak nieprzejmujmy się tym.
2.2 Struktury danych
Spróbujmy lepiej zorganizować bazę danych i dodać kilka reguł. Niech osobybędą opisywane predykatem osoba/2, której argumentami są imię oraz ilośćposiadanej gotówki, zaś napoj/3 będzie zawierał informacje o nazwie napoju,jego charakterze (zimny, gorący) i cenie.
| ?- [user].compiling user for byte code...osoba(jan,20).osoba(tomasz,5).osoba(adam,3).
5
lubi(jan, herbata).lubi(jan, piwo).lubi(tomasz, piwo).lubi(tomasz, kawa).lubi(adam, kawa).
napoj(piwo,zimny,6).napoj(herbata,goracy,2).napoj(kawa,goracy,4).
moze_kupic(X,Y) :-osoba(X,Kwota),napoj(Y,_,Cena),Cena =< Kwota .
user compiled, 19 lines read - 1701 bytes written, 107623 ms
(4 ms) yes| ?-
Przyjrzyjmy się regułom. Każda reguła składa się z dwóch części: głowyi ciała, deklaracji i definicji, oddzielonych od siebie symbolem ’:-’, którypełni rolę operatora relacji implikacji, tyle, że zapisanej odwrotnie. Regułęmoze kupic/2 należy interpretować następująco:
JEŻELI (osoba X dysponuje kwotą Kwota) ∧ (napoj Y kosztuje Cena)∧ ( Kwota ≥ Cena ) TO (osoba X moze kupic napój Y ).
Należy zwrócić uwagę na symbol ’ ’ w predykacie napoj/3 użytym wewnątrzreguły. Oznacza on, że ukonkretniona wartość zmiennej, która powinnaznaleźć się w tym miejscu, nie jest dla nas interesująca. Poeksperymentujmy:
| ?- napoj(X,_,Y), Y=<3.
X = herbataY = 2 ? ;
no| ?- moze_kupic(X,piwo).
X = jan ? ;
no| ?- moze_kupic(jan,X).
6
X = piwo ? ;
X = herbata ? ;
X = kawa
yes
2.3 Arytmetyka
Jako operator przypisania, uzywany jest predykat is/2:
| ?- X is 2.
X = 2
yes| ?- X is 7+8.
X = 15
yes| ?- X = 7+8.
X = 7+8
yes| ?- X=3, Y=4, Z is sqrt(Y*Y + X*X).
X = 3Y = 4Z = 5.0
yes
3 Listy
Listy stanowią podstawową złożoną strukturę danych. Dostęp do elementówlisty nie jest jednak taki prosty. Bezpośredni dostęp jest w zasadzie tylko dopierwszego elementu (głowy). Żeby dostać się do dalszego elementu trzebalistę przetwarzać rekurencyjnie. Operator ’|’ dzieli listę na głowę i ogon.
7
3.1 Lista i jej elementy
Przyjrzyjmy się przykładom, które mówią same za siebie:
| ?- [H|T] = [a,b,c,d,e].
H = aT = [b,c,d,e]
yes| ?- [H|T] = [[a,b,c],d,e,f,g,[h,i]].
H = [a,b,c]T = [d,e,f,g,[h,i]]
yes| ?- [H|T] = a.
no| ?- [H|T] = [a].
H = aT = []
yes| ?- [A|[B|C]] = [a,b].
A = aB = bC = []
yes| ?- [_|[_|[_|[X|_]]]] = [1,2,3,4,5,6,7,8].
X = 4
yes
3.2 Operacje na listach
Podstawowe operacje na listach dostępne są w każdej implementacji Prologu.Jeżeli nawet nie — łatwo można je zdefiniować. Oto potęga rekurencji:
length([],0).length([_|T], X) :- length(T, Y), X is Y+1.
8
append([],X,X).append([H|T],L1,[H|L2] :- append(T,L1,L2).
last([X|[]],X).last([_|T], X) :- last(T, X).
Jak zachowują się te predykaty?
| ?- last([a,b,c,d,e],X).
X = e
yes| ?- last([H|T], Y).
T = []Y = H ? ;
T = [Y] ? ;
T = [_,Y] ? ;
T = [_,_,Y] ? ;
T = [_,_,_,Y] ? ;
T = [_,_,_,_,Y] ? <RETURN>yes| ?- append([1,2,3],[a,b,c],Y).
Y = [1,2,3,a,b,c]
yes| ?- append(X,[a,b,c],[o,a,b,c]).
X = [o] ? ;
no| ?- append(X,Y,[a,b,c]).
X = []Y = [a,b,c] ? ;
X = [a]
9
Y = [b,c] ? ;
X = [a,b]Y = [c] ? ;
X = [a,b,c]Y = [] ? ;
no| ?- append([a,b],[a,b],Y).
Y = [a,b,a,b]
yes| ?- length([a,b,c], 3).| ?- length([a,b,c],3).
yes| ?- length([a,b,c],4).
no| ?- length([a,b,c],X).
X = 3
yes| ?- length(X,3).
X = [_,_,_] ? ;
OOPS! Przestało działać. Dlaczego Prolog poszukiwał kolejnego rozwiąza-nia? Jakiego rozwiązania? Wyjaśnię to w jednym z kolejnych rozdziałów.
3.3 Jeszcze o listach
Oto klasyczny przykład, jak obliczyć sumę elementów listy:
| ?- [user].compiling user for byte code...sum([],X,X).sum([H|T],X,Y) :- Z is H + X, sum(T,Z,Y).sum(List, X) :- sum(List, 0, X).
user compiled, 4 lines read - 826 bytes written, 167521 ms
10
(4 ms) yes| ?- sum([1,2,3,4,5],X).
X = 15
yes
Czy można to zrobić inaczej? Zapewne tak! Byłbym zdziwiony, gdyby niebyło innej metody. Zatem warto spróbować znaleźć lepsze rozwiązanie.
4 Grafy
Chyba już wystarczy prostych, w sumie banalnych przykładów. Czas zająćsię czymś pożytecznym. Wiele sytuacji, zjawisk czy zdarzeń, z którymi mado czynienia programista da się zamodelować w postaci grafu: siatka połą-czeń drogowych, drzewo rozbioru gramatycznego, przebieg procesu techno-logicznego itp. Spróbujmy zatem na prostym przykładzie zaprzęgnąć Prologdo „inteligentnej” pracy.
4.1 Ścieżki w grafie nieskierowanym
Oto fragment sieci dróg krajowych (mam nadzieję, że w niedalekiej przy-szłości Droga Krajowa będzie oznaczała drogę szybkiego ruchu) lekko do-stosowany do potrzeb dydaktycznych.
11
Przecież to jest graf (nieskierowany). Model systemu drogowego w Pro-logu może wyglądać następująco: predykat droga/4 będzie opisany następu-jącymi argumentami: skąd (nazwa miasta), dokąd (nazwa miasta), odległośći numer drogi (na rysunku w nawiasie).
4.2 Graf skierowany
4.3 Drzewo (genealogiczne)
5 Prosty system ekspertowy
12