Paradygmaty Programowania

Transkrypt

Paradygmaty Programowania
Paradygmaty Programowania
Wykład 8, Programowanie w logice (wprowadzenie)
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
1
Anegdota
Jak odpowiedzają programiści pytani o drogę? Ot, zobaczmy sami:
Piszący w Pascalu (język proceduralny):
Pójdzie pan do tego skrzyżowania, skręci pan w prawo w ulice:
Mickiewicza, dalej ulicą: Prusa, a potem skręci pan w lewo w:
Żeromskiego i już pan jest na miejscu.
Piszący w Javie lub C# (języki obiektowe):
Weź Pan taksówkę.
Piszący w Haskellu (język funkcyjny):
Dojdzie pan do tego skrzyżowania, a dalej się pan zapyta.
Piszący w Prologu (język logiki):
Widzi pan tamten wysoki budynek z wielkim czerwonym szyldem? To tam.
Taki właśnie jest język logiki: opisujemy co chcemy rozwiązać, a nie jak rozwiązać
- problem rozwiązuje za nas sam język.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
2
Wprowadzenie
Programowanie w logice ogólnie rzecz biorąc polega na zapisywaniu
stwierdzeń używając rachunku predykatów pierwszego rzędu. Wyniki
powstają jako rezultat (automatycznego) wnioskowania.
Języki programowania w logice nazywane są także językami
deklaratywnymi, gdyż programy w nich pisane w odróżnieniu od języków
imperatywnych nie składają się z podstawień i sterowania przepływem, a
z deklaracji.
Jedynym rozpowszechnionym i stosowanym językiem umożliwiającym
tego typu programowanie jest Prolog.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
3
Rachunek predykatów pierwszego rzędu
Posługujemy się rachunkiem predykatów w takiej formie w jakiej jest on
zaimplementowany w Prologu. A zatem kilka podstawowych pojęć:
Term to funktor (symbol stanowiący nazwę relacji) z listą parametrów
atomowych, np. człowiek(jerzy), lubi(adam, jabłko).
Stała jest szczególnym przypadkiem termu (bezparametrowym), np.
jerzy, adam, jabłko.
Stwierdzenie to jeden lub więcej termów połączonych spójnikami:
¬ negacja, ∨ alternatywa, ∧ koniunkcja, ⇔ równoważność
i ⇒ implikacja
W stwierdzeniach mogą pojawiać się zmienne (np. X, Y) związane
kwantyfikatorami (uniwersalnym ∀ lub egzystencjalnym ∃).
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
4
Klauzule
W Prologu nie używa się kwantyfikatorów; w zamian za to stwierdzenia zapisuje się w postaci
klauzul. Ogólna postać klauzuli jest następująca:
A1 ∧ A2 ∧ ... ∧ Am ⇒ B1 ∨ B2 ∨ ... ∨ Bn
gdzie Ai i Bi to termy. I tak:
Wyrażenie A1 ∧ A2 ∧ ... ∧ Am to poprzednik klauzuli.
Wyrażenie B1 ∨ B2 ∨ ... ∨ Bn to następnik klauzuli.
Każde stwierdzenie można zapisać w formie klauzuli - istnieje algorytm, który to robi. Prawa strona
klauzuli nazywa się często głową, a lewa ciałem klauzuli.
W Prologu używa się klauzul w postaci tzw. klauzul Horna, czyli takich, które w następniku mają zero
lub jeden term (n = 0 lub n = 1).
I tak:
Klauzule postaci (n = 1 i m > 0):
A1 ∧ A2 ∧ ... ∧ Am ⇒ B1
nazywa się regułami.
Klauzule postaci (n = 1 i m = 0):
B1
nazywamy faktami.
Klauzule postaci (n = 0):
A1 ∧ A2 ∧ ... ∧ Am
nazywamy celem, czyli tym co chcemy udowodnić.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
5
Rezolucja (Robinson, 1965) i Unifikacja
Rezolucja to metoda wnioskowania do stosowanych tutaj stwierdzeń.
Podstawowa jej reguła jest następująca: wiedząc że
P ⇒ Q oraz R ⇒ S
wnioskujemy że
P ⇒ S
o ile tylko Q i R dają się zunifikować, tj. znajdziemy takie wartości dla zmiennych
od których zależą, dzięki którym uzyskamy równość: Q = R
Z technicznego punktu widzenia, osiąga się to przez wyliczenie P ∧ R oraz
Q ∧ S i usunięcie termów, które występują po obydwu stronach wyrażenia.
Występowanie zmiennych w stwierdzeniach powoduje, że w trakcie rezolucji
trzeba znaleźć takie wartości dla tych zmiennych, które pozwolą na odpowiednie
dopasowanie.
Podstawianie pod zmienne tymczasowych wartości pozwalających na unifikację
zwane jest instancjonowaniem. Mówimy też o utożsamieniu zmiennej z wartością.
Unifikacja często wymaga nawrotów, tj. zmienna jest instancjonowana, lecz
dopasowanie nie udaje się i wówczas zmienną instancjonuje się inną wartością.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
6
Dowodzenie twierdzeń przez rezolucję
W krokach stosowanie rezolucji wygląda następująco:
Tworzymy zbiór stwierdzeń zawierający założenia twierdzenia
(hipotezy) i negację tezy twierdzenia (cel).
Hipotezy można uważać za bazę danych.
Za pomocą rezolucji dochodzimy do sprzeczności.
To dowodzi twierdzenia.
Zasada jest prosta, ale złożoność czasowa może być barierą nie do
pokonania...
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
7
PROLOG
Prolog wywodzi się z prac badawczych prowadzonych w latach 70-tych
XX wieku w Marsylii i w Edynburgu. Opracowany został z myślą o
przetwarzaniu języka naturalnego i automatycznym dowodzeniu
twierdzeń. W latach 80-tych stworzono też wersje na (ówczesne)
mikrokomputery, np. bardzo udaną wersję micro-Prolog.
Prolog to język programowania logicznego, lub dokładniej
programowania w logice a jego nazwa pochodzi od: PROgrammation
LOGique, PROgramming in LOGic.
Obecnie dostępnych jest szereg implementacji języka Prolog, w tym
swobodnie dostępne kompilatory Prologu, to:
Jan Wielemaker, SWI-Prolog, http://www.swi-prolog.org
Daniel Diaz, GNU-Prolog, http://gnu-prolog.inria.fr
Na ćwiczeniach korzystać będziemy z dystrybucji SWI-Prolog.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
8
Termy w Prologu
Term w Prologu to stała, zmienna lub struktura (termy złożone).
Stała może być atomem (składnia - jak typowy identyfikator lub napis
ujęty w apostrofy) lub liczbą całkowitą.
Nazwy zmiennych zaczynają się od dużej litery.
Instancjonowanie zmiennych następuje wyłącznie w trakcie rezolucji.
Trwa tylko do czasu osiągnięcia celu.
Struktura ma postać
funktor(listaParametrów)
gdzie parametry są atomami, zmiennymi lub innymi strukturami.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
9
Stwierdzenia w Prologu
Stwierdzenia występują w dwóch formach, odpowiadających
klauzulom Horna z głową.
Pojedyncza struktura z kropką na końcu, to fakt (tzn. zakładamy, że
stwierdzenie jest prawdziwe), np.
ojciec(jan, janusz).
Stwierdzenie zawierające następnik będący pojedynczą strukturą i
poprzednik będący strukturą lub koniunkcją kilku struktur, to reguła;
zapisujemy ją z kropką na końcu, a następnik od poprzednika
oddzielamy znakiem ”:-” (traktowanym jako implikacja), np.
dziadek(X, Z) :- ojciec(X, Y), ojciec(Y, Z).
Można przyjąć, że przed każdą klauzulą stoi niejawny kwantyfikator
uniwersalny, wiążący zmienne tej klauzuli.
Program w języku Prolog, to nic innego, jak zbiór stwierdzeń takich, jak
opisano powyżej.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
10
Cel w Prologu
Stwierdzenie, które chcemy udowodnić (lub obalić, tzn. udowodnić
jego fałszywość), nazywamy celem.
Formalnie jest to klauzula bez głowy; składniowo wygląda tak samo,
jak fakt.
Rozróżnienie pomiędzy celem a faktem (będącym częścią programu)
bierze się z trybu wpisywania w interpreterze.
Po wpisaniu celu interpreter Prologu odpowie true lub false.
Odpowiedź true oznacza, że udało się udowodnić cel przy podanych
w programie założeniach.
Odpowiedź false oznacza, że udało się obalić cel lub że nie udało się
go udowodnić przy podanych założeniach.
Jeśli cel jest stwierdzeniem złożonym, każda z zawartych w nim
struktur zwana jest podcelem.
Cel może zawierać zmienne. W takim przypadku Prolog znajdzie
instancje, dla których cel jest prawdziwy.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
11
Rezolucja w Prologu
Prolog stosuje metodę zstępującą, tzn. zaczyna od celu i próbuje
znaleźć ciąg pasujących stwierdzeń, które prowadzą do pewnego zbioru
faktów w programie.
Ów ciąg stanowi dowód celu.
Zauważmy, że można by stosować metodą wstępującą, tzn. zacząć od
faktów w programie i próbować dojść do celu.
Metoda wstępująca byłaby dobra przy dużym zbiorze rozwiązań; przy
małym lepsza jest metoda zstępująca.
Dla stwierdzeń złożonych używane jest przeszukiwanie w głąb, tzn.
najpierw znajduje się dowód dla pierwszego podcelu, a potem
przetwarza się następne podcele.
Jeśli Prologowi nie uda się udowodnić jednego z podcelów, porzuca
ów podcel i wraca do poprzednich podcelów, próbując znaleźć
alternatywne rozwiązania. Proces ten zwany jest nawracaniem.
Przeszukiwanie bazy danych postępuje zawsze od pierwszego
stwierdzenia do ostatniego.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
12
Arytmetyka w Prologu
W oryginalnym Prologu dostępna jest tylko arytmetyka na liczbach
całkowitych. W wersji SWI-Prolog mamy możliwość pracy na liczbach
zmiennoprzecinkowych.
Dla uproszczenia zapisu wprowadzono infiksowe operatory
arytmetyczne, np.:
+,-,*,/,//
i operator is. Stosuje się go do zunifikowania zmiennej (która musi być
nie zainstancjonowana) z wartością wyrażenia arytmetycznego.
Przykład:
X is 2 * Y + 3
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
13
Przykład arytmetyki w Prologu
Powiedzmy, że dysponujemy bazą która zawiera informacje o:
pokonanym dystansie (np. w metrach) przez chłopców: janek, michał i tomek:
dystans(janek, 9600).
dystans(michał, 8100).
dystans(tomek, 13500).
oraz czasie (np. w minutach) pokonania tych dystansów:
czas(janek, 120).
czas(michał, 90).
czas(tomek, 135).
Dołączamy teraz do bazy funktor
prędkość(X, Y)
który pozwala obliczyć średnią prędkość osoby X (lub sprawdzić, że wynosi ona
Y).
prędkość(X, Y) :- czas(X, C), dystans(X, D), Y is D/C.
Umieszczenie struktury Y is D/C jako ostatniej jest istotne; dzięki temu Prolog
najpierw instancjonuje zmienne D i C przez dopasowanie do faktów, a potem
wylicza (lub sprawdza) wartość Y.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
14
Przykład arytmetyki w Prologu
Uruchomienie przykładu z poprzedniego slajdu umożliwi teraz zadawanie
zapytań:
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
15
Listy w Prologu
Listy w Prologu to dowolnej długości ciągi elementów (atomów,
termów, a także innych list).
Składnia podobna jest do Haskella: nawiasy kwadratowe, elementy
rozdzielone przecinkami.
Nie ma jawnych funkcji do tworzenia i rozkładania list.
Z uwagi na dopasowywanie, wystarcza odpowiednie użycie zapisu
[X | Y]
gdzie X jest głową, a Y ogonem listy.
Np.
X=[a,b,c].
Y=[2,4,6,ala].
Z=[].
X=[”ala”,123].
[X|Y]=[1,2,3].
[X,Y|Z]=[3,4,”ala”].
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
16
Listy - wywołanie przykładu
Wywołanie z poprzedniego slajdu da następujące rezultaty:
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
17
Listy - kilka przykładów predykatów
Obliczanie długości listy:
dlugosc([],0).
dlugosc([_|Ogon],Dlug) :- dlugosc(Ogon,X), Dlug is X+1.
Sprawdzenie czy jakiś element należy do listy:
czyNalezy(X,[X,_]).
czyNalezy(X,[_,Y]) :- czyNalezy(X,Y).
Sklejanie dwóch list:
sklej([],X,X).
sklej([X|L1],L2,[X|L3]) :- sklej(L1,L2,L3).
Tutaj znak _ oznacza zmienną anonimową, która może być dopasowana do
czegokolwiek.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
18
Listy - wywołanie przykładów
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
19
Interpreter - jak to właściwie działa
Przypuśćmy, że dysponujemy następującą bazą wiedzy:
lubi(jan, tatry).
lubi(jan, beskidy).
lubi(jerzy, beskidy).
lubi(jerzy, bieszczady).
lubiToSamo(X, Y) :- lubi(X, S), lubi(Y, S), X \= Y.
Co się dzieje, gdy zadamy pytanie lubiToSamo(jan, X)?
Następuje utożsamienie zmiennej X w definicji lubiToSamo(...) z atomem jan; sytuacja jest zatem
taka, jakbyśmy mieli;
lubiToSamo(jan, Y) :- lubi(jan, S), lubi(Y, S), jan \= Y.
Interpreter szuka teraz dowodu dla pierwszego podcelu, czyli lubi(jan, S). Pierwsza możliwość to
utożsamienie S z atomem tatry.
Interpreter przechodzi do dowodu drugiego podcelu, czyli teraz lubi(Y, tatry). Jedyna możliwość to
utożsamienie Y z atomem jan.
W tej sytuacji trzeci podcel (X \= Y) nie daje się udowodnić. Potrzebny jest nawrót.
Nawrót do drugiego podcelu nic nie daje - nie ma alternatywnego instancjonowania dla Y, a S zostało
zainstancjonowane wcześniej.
Następuje zatem nawrót do pierwszego podcelu i alternatywne instancjonowanie zmiennej S na atom
beskidy.
W kolejnym kroku mamy ponownie utożsamienie Y z atomem jan i ponownie nawrót przy X \= Y.
Tym razem jednak wystarcza nawrót do drugiego podcelu i alternatywne instancjonowanie Y na jerzy.
Trzeci podcel jest teraz spełniony (X \= Y) i mamy: X = jan, Y = jerzy, S = beskidy i odpowiedź
interpretera X = jerzy.
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
20
Dziękuję za Uwagę!!!
☺
dr Robert Kowalczyk, Katedra Analizy
Nieliniowej, WMiI UŁ
21

Podobne dokumenty