Program, który się uczy.
Transkrypt
Program, który się uczy.
Program, który się uczy. Korzystając z omówionych poleceń, jesteśmy w stanie zaprojektować program, który nauczy się nowych informacji do swojej bazy wiedzy, a także zapisze je w taki sposób, aby mogły być dostępne w następnej sesji. W skrócie, program działa następująco: 1. Wczytujemy bazę wiedzy poprzez reconsult 2. Wywołujemy funkcję która pyta o kraj i wczytujemy tekst z klawiatury (read). 3. W przypadku otrzymania ciągu znakowego „stop.” program przełącza się w zapis do pliku bazy wiedzy (tell), następuje listing predykatów, told zamyka plik i kończy się działanie programu. 4. W zależności od tego, czy podany kraj jest już w naszej bazie, wyświetlamy jego stolicę lub pytamy o nią, dopisując nową informację poprzez assertz. 5. Przechodzimy ponownie do kroku drugiego. Program, który się uczy. Kod programu: start :- reconsult('baza.pl'), nl, write('Podaj nazwę kraju z małej litery i z kropką na końcu.'), nl, write('Napisz "stop." aby przerwać działanie programu.'), nl, nl, zapytanie. zapytanie :- write('Kraj? '), read(Kraj), odpowiedz(Kraj). odpowiedz(stop) :- write('Zapisywanie bazy wiedzy...'), nl, tell('baza.pl'), listing(stolica), told, write('Gotowe!'),nl. Program, który się uczy. Kod programu (c.d.): odpowiedz(Kraj) :- stolica(Kraj,Miasto), write('Stolicą '), write(Kraj), write(' jest '), write(Miasto),nl, nl, zapytanie. odpowiedz(Kraj) :- \+ stolica(Kraj,_), write('Nie znam stolicy tego kraju.'),nl, write('Proszę, podaj mi ją!'),nl, write('Stolica? '), read(Miasto), write('Dziękuję.'),nl,nl, assertz(stolica(Kraj,Miasto)), zapytanie. Program, który się uczy. Przykładowe wywołanie programu. Podaj nazwę kraju z małej litery i z kropką na końcu. Napisz "stop." aby przerwać działanie programu. Kraj? polska. Stolicą polska jest warszawa Kraj? dania. Stolicą dania jest kopenhaga Kraj? rosja. Nie znam stolicy tego kraju. Proszę, podaj mi ją! Stolica? moskwa. Dziękuję. Kraj? rosja. Stolicą rosja jest moskwa Kraj? stop. Zapisywanie bazy wiedzy... Gotowe! true Wejście/wyjście znaków (get, get0, put) Wbudowany predykat put wyświetla znak ASCII podany jako liczbę. Przykład: ?- put(42). * True. Przeciwieństwem put jest get który pobiera znak od użytkownika i wyświetla jego liczbową reprezentację. Przykład: ?- get(X). |: Ę X = 280. Wejście/wyjście znaków (get, get0, put) W zależności od interpretera Prologa, program może się zachowywać w dwa sposoby. Pierwszym z nich jest wczytanie buforowane. Znak zostanie odczytany dopiero po wciśnięciu i zatwierdzeniu go klawiszem enter. W przypadku wejścia klawiatury, które nie jest buforowane, znak zostanie wczytany bezpośrednio po jego wciśnięciu. Predykat get automatycznie opuszcza znaki puste, nowej linii (enter), lub inne które są znakami specjalnymi, których się nie da wyświetlić. Jeżeli chcemy wczytać dowolny znak, istnieje dodatkowy predykat get0. Przykład: ?- get0(X), get0(Y). |: Ω X = 937, Y = 10. Znak nowej linii \n (10) jest poprzedzony znakiem omega (w reprezentacji UTF8). Wejście/wyjście znaków (get, get0, put) Tablica znaków ASCII (7bit) Wejście/wyjście znaków (get, get0, put) Standard ISO definiuje jedynie put_code (odpowiednik put) oraz get_code (odpowiednik get0). Predykat get, może być zdefiniowany w sposób następujący: get(Code) :- repeat, get_code(Code), Code>32, !. Zgodnie z tablicą kodów ASCII pomijamy znaki o wartości mniejszej lub równej 32. Tworzenie menu wyboru Dzięki funkcji wczytującej znak od użytkownika, możemy napisać proste menu wyboru. Oto przykładowy kod: stolica(polska, warszawa). stolica(niemcy, berlin). stolica(szwecja, sztokholm). stolica(dania, kopenhaga). start :- pokaz_menu, pobierz_z_menu(Stolica), stolica(Stolica,Miasto), nl, write('Stolicą '), write(Stolica), write(' jest '), write(Miasto), nl. pokaz_menu :- write('O jakim kraju chcesz się dowiedzieć?'),nl, write(' 1 Polska'), nl, write(' 2 Niemcy'), nl, write(' 3 Szwecja'), nl, write(' 4 Dania'), nl, write('Podaj liczbe od 1 do 4 -- '). Tworzenie menu wyboru Kod (c.d.) pobierz_z_menu(Stolica) :- get(Code), % wczytaj znak get0(_), % opuść znak nowej linii interpretuj(Code,Stolica). interpretuj(49,polska). interpretuj(50,niemcy) . interpretuj(51,szwecja). interpretuj(52,dania). /* /* /* /* ASCII ASCII ASCII ASCII 49 50 51 52 Przykładowe wywołanie: ?- start. O jakim kraju chcesz się dowiedzieć? 1 Polska 2 Niemcy 3 Szwecja 4 Dania Podaj liczbe od 1 do 4 -- 1 Stolicą polska jest warszawa true. = = = = '1' '2' '3' '4' */ */ */ */ Tworzenie menu wyboru Inne prostsze menu „Tak lub Nie” można zaimplementowane następująco: tak_lub_nie(Result) :- get(Char), get0(_), interpret(Char,Result), !. % wczytaj znak tak_lub_nie(Result) :- nl, write('Y czy N?:'), tak_lub_nie(Result). interpret(89,yes). interpret(121,yes). interpret(78,no). interpret(110,no). % % % % ASCII ASCII ASCII ASCII 89 121 78 110 = = = = 'Y' 'y' 'N' 'n' W tym przypadku menu powtarza się dopóki nie podamy litery Y, y, N, lub n. Wywołanie: ?- write('Czy chcesz kontynuować? [Y/N] '), tak_lub_nie(X). Czy chcesz kontynuować? [Y/N] x Y czy N?:n X = no. Systemy ekspertowe Używając języka Prolog możemy wykonać w prosty sposób system ekspertowy. Można w nim wyróżnić poniższe elementy: interfejs użytkownika ● baza wiedzy ● mechanizm wnioskujący, który odnajduje rozwiązanie/odpowiedź ● mechanizm wyjaśniający, dlaczego jest to odpowiedź poprawna/dopuszczalna ● Systemy ekspertowe Używając języka Prolog możemy wykonać w prosty sposób system ekspertowy. Można w nim wyróżnić poniższe elementy: interfejs użytkownika ● baza wiedzy ● mechanizm wnioskujący, który odnajduje rozwiązanie/odpowiedź ● mechanizm wyjaśniający, dlaczego jest to odpowiedź poprawna/dopuszczalna ● Systemy ekspertowe Jednym z przykładowych systemów ekspertowych, jest diagnozowanie przyczyny awarii samochodu. W przypadku tego systemu elementy systemu ekspertowego są odwzorowane przez następujące predykaty: problem/1 → baza wiedzy probuj_wszystkie_mozliwosci/0 → wnioskujący wytlumacz/1 → mechanizm wyjaśniania zadaj_pytanie/1 i uzytkownik_mowi/2 użytkownika mechanizm → interfejs Systemy ekspertowe Program diagnozowanie przyczyny awarii samochodu w Prologu ma dwie zalety, które były by trudne do zaimplementowania w konwencjonalnych językach programowania. ● ● wyświetla on listę wszystkich możliwych diagnoz, nie tylko jedną nie pyta użytkownika o informacje, które nie są w danej chwili potrzebne Aby to osiągnąć, program nie wywołuje bezpośrednio predykatu zapisana_odp, ale używa uzytkownik_mowi, który albo pozyskuje odpowiedź użytkownika z obecnej bazy wiedzy, albo pyta się go o nią. Systemy ekspertowe Kod programu: :- reconsult('taknie.pl'). start :write('Program diagnozuje Odpowiadaj na pytania Y lub N'), wyczysc_odpowiedzi, probuj_wszystkie_mozliwosci. dlaczego samochód nie chce ruszyć. probuj_wszystkie_mozliwosci :problem(D), wytlumacz(D), fail. % wróć się do wszystkich mozliwosci probuj_wszystkie_mozliwosci. % ...następnie zakończ. Systemy ekspertowe Kod programu: problem(rozladowana_bateria) :uzytkownik_mowi(rozruch_byl_ok,yes), uzytkownik_mowi(rozruch_jest_ok,no). problem(rozladowana_bateria) :uzytkownik_mowi(rozruch_byl_ok,yes), uzytkownik_mowi(rozruch_jest_ok,no). problem(zly_bieg) :uzytkownik_mowi(rozruch_byl_ok,no). problem(uklad_rozruchu) :uzytkownik_mowi(rozruch_byl_ok,no). problem(uklad_paliwa) :uzytkownik_mowi(rozruch_byl_ok,yes), uzytkownik_mowi(paliwo_jest_ok,no). problem(uklad_zaplonowy) :uzytkownik_mowi(rozruch_byl_ok,yes), uzytkownik_mowi(paliwo_jest_ok,yes). Systemy ekspertowe Kod programu: wyczysc_odpowiedzi :- retract(zapisana_odp(_,_)),fail. wyczysc_odpowiedzi. uzytkownik_mowi(Q,A) :- zapisana_odp(Q,A). uzytkownik_mowi(Q,A) :- \+ zapisana_odp(Q,_), nl, nl, zadaj_pytanie(Q), tak_lub_nie(Odp), asserta(zapisana_odp(Q,Odp)), Odp = A. zadaj_pytanie(rozruch_byl_ok) :- write('Czy rozrusznik odpalał samochód na początku normalnie?\n'). zadaj_pytanie(rozruch_jest_ok) :- write('Czy rozrusznik odpala nadal normalnie?\n'). zadaj_pytanie(paliwo_jest_ok) :- write('Zajrzyj go gaźnika. Czy widzisz lub czujesz benzyne?\n'). Systemy ekspertowe Kod programu: wytlumacz(zly_bieg) :write('Samochód może być na złym biegu.'), nl. wytlumacz(uklad_rozruchu) :write('Sprawdź baterie, akumulator i alternator.\nJeżeli wszystko jest OK, problemem może być rozrusznik.'), nl. wytlumacz(rozladowana_bateria) :write('Twoje próby odpalenia samochodu rozładowały akumulator. Naładuj go.'), nl. wytlumacz(uklad_paliwa) :write('Sprawdź czy masz paliwo w baku. Jeśli tak sprawdź czy przewód lub filtr paliwowy nie jest zatkany. Może to być też problem pompy paliwowej.'), nl. wytlumacz(uklad_zaplonowy) :write('Sprawdź świece zapłonowe, kable i inne elementy układu zapłonowego. Jeśli jakakolwiek część wygląda na uszkodzoną lub straciła date ważności - wymień ją. W przypadku nierozwiązania problemu skontaktuj się z mechanikiem.'), nl. :- dynamic(zapisana_odp/2).