38
Odczytywanie danych w różnych formatach

Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Embed Size (px)

Citation preview

Page 1: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Odczytywanie danych w różnych formatach

Page 2: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Problem INPUT-a w wolnej formie

W tym rozdziale pokażemy, jak sprawić, aby Prolog przeczytał dane w dowolnym wybranym formacie; na końcu przedstawimy program w Prologu, który odczytuje arkusz kalkulacyjny. Ilość uwagi poświęconej temu działowi będzie zależała od potrzeb. Nie ma tu logicznej teorii programowania - tylko praktyczne algorytmy. Jeśli jesteś eksperymentatorem AI, możesz podejść do niego z lekkim dystansem, powracając później jeśli zajdzie taka potrzeba. Lecz jeśli jesteś producentem oprogramowania komercyjnego, może to być rozdział który uczyni Prolog użytecznym dla twojej aplikacji. Procedury określone w niniejszym rozdziale nie są przeznaczone do pracy na oślep. Są tu zawarte pouczające przykłady, i zakładamy, że przed wprowadzeniem ich do waszych programów, będziecie studiowali, jak one działają i bardziej dokładnie dostosowywali je do swoich potrzeb.

Page 3: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Konwertowanie stringów i liczb atomowych

Zacznijmy od wejścia klawiatury(input). Chcielibyśmy, aby użytkownik mógł wpisać cokolwiek z klawiatury i aby poszło to w Prologu jako atom lub liczba, np.:

?- read_atom(What). this is an atom (typed by user) What = 'this is an atom' ?- read_num(What). 3.1416 (typed by user) What = 3.1416 Aby to zadziałało, będziemy polegać na read_str, określonym

w rozdziale 3 i pokazanym wraz z innymi predykatach.

Page 4: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Istnieją trzy sposoby podejścia do procesu konwersji:

• Po pierwsze, ISO Prolog Standard definiuje dwa wbudowane predykaty, atom_codes i number_codes, które przechodzą, odpowiednio, atomy w stringi i liczby w stringi, np.:

?- atom_codes(abc,What). What = [97,98,99] ?- atom_codes(What,"abc"). What = abc ?- number_codes(3.14,What). What = [51,46,49,52] ?- number_codes(What,"3.14"). What = 3.14 Jeśli number_codes daje stringa, który nie podaje prawidłowego

numeru, lub jeżeli jeden z jego argumentów jest niewłaściwego typu, wywołuje to stan runtime error.

Page 5: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Istnieją trzy sposoby podejścia do procesu konwersji:

• Wiele starszych Prologów używa name/2 aby wykonać oba rodzaje konwersji: ?- name(abc,What). What = [97,98,99] ?- name(What,"abc"). What = abc ?- name(3.14,What). What = [51,46,49,52] ?- name(What,"3.14"). What = 3.14 Ale istnieje kilka Prologów w których name posiada zachowanie określone dla kodów atomowych i

konwersja liczb odbywa się w zupełnie inny sposób. Ponadto, name ogólnie radzi sobie z błędami, po prostu nie kompilując się, ale należy sprawdzić implementację aby się o tym upewnić. W dalszej części zakładamy, że już z powodzeniem wdrożony read_atom i read_num, zdefiniowano mniej więcej w następujący sposób:

read_atom(Atom) :- read_str(String), atom_codes(Atom,String). read_num(Num) :- read_str(String), number_codes(Number,String). i że są one w pliku READSTR.PL (Przykład 5.1). Pamiętajmy, aby sprawdzić, czy instrukcja działa poprawnie w twoim Prologu.

Page 6: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Istnieją trzy sposoby podejścia do procesu konwersji:

• Jest jeszcze trzeci sposób konwersji stringów na liczby: wybierać cyfry jedna po drugiej, konwertować je do ich wartości liczbowych i wykonać obliczenia. W szczególności to, co robimy to utrzymanie sumy bieżącej, która rozpoczyna się od 0. Za każdym razem dostajemy inną cyfrę, mnożymy sumę przez 10, a następnie dodajemy do niej wartość tej cyfry. Na przykład, konwertując string "249", należy wykonać następujące obliczenia:

Digit ‘2’ (0 10) + 2 = 2 Digit ‘4’ (2 10) + 4 = 24 Digit ‘9’ (24 10) + 9 = 249 Możemy nigdy nie musieć używać tego sposobu, ale algorytm jest

warty zapamiętania w przypadku, gdy będziemy mieli do czynienia z nietypowym bazami lub formatami (np. system szesnastkowy, lub liczby rozdzielone przecinkami).

Page 7: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

• przyklady

Page 8: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Łączenie naszego kodu z wyrazami

Załóżmy, że piszemy program, w którym chcemy używać predykatów, które są zdefiniowane w READSTR.PL. Mamy dwie możliwości:

– Możemy kopiować READSTR.PL, cały, do swojego programu. – Możemy wstawić dyrektywę w programie, która powoduje, że READSTR.PL jest przetwarzany. Drugim z nich jest to, co będziemy badać tutaj. W ISO Prolog oraz w

wielu istniejących implementacjach (w tym Quintus, SWI i LPA), dyrektywa :- ensure_loaded('readstr.pl'). oznacza: "Jeśli READSTR.PL nie jest jeszcze skonsultowany , skonsultuj go teraz.„

(Oczywiście, to działa tylko wtedy, gdy READSTR.PL jest w bieżącym katalogu.) Inne Prologi nie mają ensure_loaded, dlatego uzasadnione jest stosowanie zastępczego reconsulta, jak poniżej:

:- reconsult('readstr.pl'). Po napotkaniu tej linii w programie, system Prolog skonsultuje READSTR.PL, nawet

jeśli READSTR.PL skonsultowano wcześniej. To trochę marnuje czas, ale nic poważnego się nie stanie.

Page 9: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Weryfikacja danych Użytkownika Za każdym razem gdy Program akceptuje od użytkownika wejście z klawiatury mogą

powstać dwa problemy:

– Użytkownik może wpisać coś, co nie jest akceptowalna odpowiedzią (np. 4 gdy wybory w menu to 1, 2 i 3).

– Użytkownik może wpisać coś, co nie jest nawet interpretowane (np. xyz, gdy oczekiwana jest liczba).

W obu przypadkach program powinien zrobić coś sensownego. Wygodne w użyciu jest zastosowanie powtarzającej się pętli aby sprawdzić poprawności danych wprowadzanych przez użytkownika, na przykład:

get_number(N) :- repeat,

write('Type a number between 1 and 3: '),

read_num(N),

N =< 3,

N >= 1,

!.

Page 10: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Weryfikacja danych Użytkownika Jeśli użytkownik wpisze numer, który jest poza zakresem, wykonanie cofa się do

powtórzenia, komputer wyświetla monit ponownie, a użytkownik dostaje jeszcze jedną szansę. Jeśli jest kilka odrębnych wyborów, wygodne jest użycie member z listy alternatyw:

get_choice(C) :- repeat, write('Type a, b, or c: '), read_atom(C), member(C,[a,b,c]), !. Nadal szybsze, ale cięższe(większe objętościowo), jest następujące podejście: get_ok(C) :- repeat, write('Type a, b, or c: '), read_atom(C), ok(C), !. ok(a). ok(b). ok(c).

Page 11: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Konstruowanie Menu Ponieważ Prolog jest dobry w obsłudze skomplikowanych struktur danych, to prostą

sprawą jest napisać procedurę, która będzie tworzyć i wyświetlać menu gdy powiesz jakie powinny być jego wybory. Przykład 5.2 przedstawia jeden przykład. Zdefiniowany tam Generator menu jest wywoływany przez zapytania, takie jak to:

?- menu([item('Georgia',ga),item('Florida',fl),item('Hawaii',hi)], What). 1 Georgia 2 Florida 3 Hawaii Your choice (1 to 3): 2 What = fl Oczywiście menu / 2 byłoby normalnie wywoływane z innej procedury, która musi

wyświetlać menu. Jej pierwszy argument składa się z listy form [item(Message1,Value1),item(Message2,Value2),item(Message3,Value3)] Z maksymalnie 9 elementów, każdy składający się z wiadomości, która ma być

wyświetlana i wyniku, który ma być zwrócony (w drugim położeniu argumentu) jeżeli użytkownik wybierze tę opcję.

Wiadomość jest zwykle atomem;"value" może być terminem jakiegokolwiek rodzaju.

Page 12: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Czytanie plików z „get byte”

Gdy przechodzimy od czytania z klawiatury do odczytu plików, pojawiają się dwa nowe problemy:

- Czy będziemy uzyskiwać każdy bajt pliku, nienaruszony i niezmieniony? - Co wydarzy się na końcu pliku? Tutaj, niestety, różne implementacje Prologu rozdzielają się. W Prologu Arity get0 po

prostu nie zadziała na końcu pliku, podczas gdy w większości innych Prologów zwraca kod -1. w ALS Prolog, get0 pomija wszystkie bajty o wartości 0 lub 13; w Cogent Prolog, get0 traktuje wartość 26 jako końcowy znak w pliku; w innych Prologach, get0 zachowuje wszystkie bajty nienaruszone.

Dla uproszczenia będziemy używać predykatu ISO Prolog get_byte / 1, który czyta każdy bajt w postaci kodu numerycznego i zwraca -1 na końcu pliku. (Różnica między get_byte i get_code jest to, że get_byte gwarantuje, ze nie będą robione jakieś specjalne manipulacje w znakach na końcach linii i na końcach plików lub w kodach których nie można wyświetlić) Jeśli get_byte nie jest wbudowany w Prologu, trzeba będzie go zdefiniować. W większości Prologów, ta definicja wystarczy:

get_byte(C) :- get0(C). bo get0 i get_byte to to samo. W Arity Prolog, użyj tej definicji zamiast: get_byte(C) :- get0(C), !. get_byte(-1).

Page 13: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Uchwytów plików (identyfikatorów Stream)

W pozostałej części tego rozdziału, będziemy wykonywać wszystkie wejścia pliku(input) przez przekierowanie standardowego inputa z poleceniem see. Robimy to niechętnie; jest to przenośne(portable), ale ryzykowne. W szczególności, w przypadku gdy program wyrzuca błąd podczas gdy wejście jest przekierowywane, możemy nie być w stanie wpisać jakichkolwiek dalszych komend do systemu Prologa. Prawie każda implementacja w Prologu to sposób, aby uzyskać dostęp do plików poprzez uchwyty (identyfikatory Stream). Na przykład,

read(H,X) zwykle oznacza to: "Wczytaj wyrażenie do X z pliku, którego uchwytem jest H."

uchwyt jest wartością, która jest dana podczas otwierania pliku. Niestety, składnia dostępu do plików w ten sposób jest znacznie zróżnicowana. Proponowany system ISO jest opisany w Załączniku A; Rzeczywista Składnia w Quintus Prolog i SWI Prolog wygląda tak:

test :- open('myfile1.txt',read,File1), read(File1,Term), close(File1), open('myfile2.txt',write,File2), write(File2,Term), close(File2). Ideą jest to, że otwarty predykat otwiera plik do zarówno czytania bądź pisania oraz

tworzy instancję File1 do uchwytu. Następnie dajemy File1 jako argument wszystkich kolejnych predykatów, które używają pliku.

Page 14: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Pola Stałej Długości

Wiele plików z danymi w świecie biznesu składa się z pól o stałej długości. Każda linia jest nazwana RECORD. Pola mogą lub nie mogą składać się ze znaków i rekordy mogą lub nie muszą kończyć się znakami na końcach linii( end–of–line). Oznacza to, że plik może lub nie być plikiem tekstowym. Odczyt pola o stałej długości jest prosty: zaczynamy od liczby bajtów która ma być odczytana i odliczamy w dół do 0, jak w przykładzie niżej:

% read_bytes(+N,-String) %%% wstępna wersja % Wczytuje „N” bitów do Stringa. read_bytes(0,[]) :- !. read_bytes(N,[C|Rest]) :- get_byte(C), NextN is N-1, read_bytes(NextN,Rest). Zauważmy, że w Prologu, często odliczamy, podczas gdy zwykły język programowania liczyłby „w górę”

(dla i = 1 do n lub podobnie). To pozwala nam porównać zmienną pętli do 0 (stała) niż porównywać do N (która ma być kolejnym parametrem dostarczonym w czasie wykonywania). Wersja read_bytes , której używamy jest bardziej skomplikowana. Wykorzystuje jeden znak LookAhead aby sprawdzić nieoczekiwany znak na końcu pliku(end-of-file). Możemy również zdefiniować skip_bytes, który pomija określoną liczba bajtów, bez zapisywania ich na liście. Większość Prologów zawiera także polecenie szukania, aby przejść do określonej pozycji w pliku o dostępie swobodnym, ale nie używamy go tutaj ze względu na brak standaryzacji. Nawet norma ISO sprawia, że szczegóły tej operacji są pozostawione realizatorowi.

Page 15: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Co teraz zrobić z danymi?

Teraz, gdy już można odczytać pliki danych z Prologa, co z nimi zrobić? W zależności od potrzeb programu, istnieją trzy główne opcje:

- Wczytać jeden rekord naraz i w jakiś sposób go przetworzyć. To jest odpowiednie podejście, jeśli celem programu jest praca i obliczanie czegoś w całym pliku.

- Wczytać każdy rekord i przekonwertować go do Prologa, który następnie jest ukazany w bazie danych. To praktyczne podejście, jeśli chcesz użyć pliku aby odpowiedzieć na zapytania oraz gdy plik (lub jego część, którą jesteś zainteresowany) nie jest zbyt duży. (wbudowane predykatywne statystyki powiedzą, ile pamięci jest dostępnej w twojej implementacji Prologa.)

- Przeszukać cały plik na dysku, gdy potrzebujesz z niego informacji. To jest najwolniejsza opcja, ale czasami jedyna praktyczna, jeśli plik jest gigantyczny. Na szczęście, większość dużych plików baz danych może być przeszukiwana sposobami które są szybsze niż wyszukiwanie sekwencyjne - każdy rekord zawiera odnośniki do innych rekordów, lub istnieje algorytm haszujący, lub oba z nich.

Page 16: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Pola rozdzielone przecinkami Przykład: 4633,"Covington","M","A","Athens","Georgia„ 5462,"Nute","D","E","Athens","Georgia" 123,"Vellino","A",,"Ottawa","Ontario„ przedstawia inny format danych który jest popularny w

przesyłaniu danych między pakietami oprogramowania : COMMA–DELIMITED FIELDS, czyli tekst z pola oddzielony przecinkami. Często, choć nie zawsze, pola alfabetyczne są zamknięte w cudzysłowie, tak że przecinki w nich nie będą traktowane jako separatory. Wczytywanie pól oddzielonych przecinkami w Prologu jest proste. Kluczem do sukcesu jest wdrożenie procedury zwanej read_until która akceptuje maxymalną ilośc bajtów oraz włącza określony kod (koniec pliku lub koniec linii, w zalezności co nastąpi wcześniej). Następnie, aby przeczytać pola rozdzielane przecinkami, po prostu wpisujemy - read_until przecinek.

Page 17: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Pola rozdzielone przecinkami

Cytaty mogą nieco skomplikować problem. Bieżący algorytm służy do tego, aby przeczytać pierwszy znak z pola, a następnie, jeśli nie jest to cytat, read_until przecinek. Lecz jeślinpole zaczyna się cytatem, komputer musi read_until zamykający cytat (tym samym uzyskując dane), a następnie read_until przecinek (aby je odrzucić). Przykład 5.6 pokazuje kompletną implementację. (wrzucic z wykladu)

Page 18: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Liczby Binarne Nie wszystkie pliki danych składają się z drukowalnych znaków. Niektóre z pól w

jakimkolwiek pliku mogą być liczbami binarnymi. Oznacza to, że na przykład numer 36, może być reprezentowany nie jako bajty dla znaków '3' i '6', ale jako liczba binarna 36 (100100). Małe liczby całkowite często przechowywane są w 16 bitach (dwóch bajtach). Na przykład:

numer 1993 jest w formacie binarnym, 0000011111001001. To nie pasuje do jednego bajta, więc jest podzielone na dwa:. 00000111 11001001.

Zadaniem programu Prolog jest to, aby przeczytać te dwa bajty (które pojedynczo mają wartości odpowiednio 7 i 201) i umieścić je razem: (7 * 256) + 201 = 1993. W efekcie traktujemy bajty jako baza-256 cyfr.

Istnieją trzy komplikacje. Po pierwsze: na maszynach zgodnych z IBM PC, bajty są przechowywane w przeciwnym porządku jakiego można by było się spodziewać: mniej znaczący bajt jest na pierwszym miejscu. (Sun SPARC umieścił bardziej znaczący bajt pierwszy.) Po drugie: liczby ujemne są reprezentowane w zapisie dwójki-dopełniacza(twos–complement), więc jeśli mamy 65535, to należy przekonwertować go na 1; Faktycznie, każda wartość większa niż 32767 jest reprezentowana przez liczbę ujemną. Po trzecie, wszelkie te obliczenia zakładają, że arytmetyka systemu Prolog nie jest ograniczona do 16 bitów przypadających na liczbę całkowitą. Praktycznie wszystkie systemy Prolog automatycznie przełączają się do zmiennoprzecinkowej arytmetyki gdzie dostępne liczby całkowite nie są już tak duże, więc nie spodziewamy się tutaj problemu. Kod do czytania podpisanych i niepodpisanych 16-bitowych liczb całkowitych przedstawiono na przykładzie 5.7.

Page 19: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Czytanie arkusza kalkulacyjnego LOTUS Przykład 5.9 przedstawia program Prolog, który odczytuje arkusze kalkulacyjne LOTUS w formacie WKS.

Ponieważ tak dużo informacji w świecie biznesu jest przechowywane w arkuszach kalkulacyjnych, bądź są one do nich łatwo importowane , taki program może znacznie przedłużyć przydatność Prologa. Walden (1986) daje pełny opis formatu WKS, który jest formatem pliku wykorzystywanym przez wczesnych wersjach Lotus'a 1-2-3. Nowsze programy arkuszy kalkulacyjnych mogą nadal używać formatu .WKS jeśli im się na to wskaże, jako, że nie jest to już ustawienie domyślnie. plik arkusza kalkulacyjnego składa się z szeregu pól (rekordów), z których każdy obejmuje:

– 16-bitowy OPCODE (kod operacji) wskazujący typ pola; – Liczba 16-bitowa podająca długość pola; – Zawartość pola, która zależy od jego rodzaju. Większość pól w zwykłym arkuszu kalkulacyjnym to NON–DATA FIELDS - to znaczy, zawierają informacje

o tym, jak wydrukować lub wyświetlić arkusz kalkulacyjny. W celu zachowania wszystkich ustawień domyślnych, pełny ich zestaw jest zapisany w nawet najmniejszym arkuszu kalkulacyjnym. W związku z powyższym, możemy zignorować prawie wszystkie „opcodes”(kody operacji). Te kody operacji, które są istotne to:

0 Początek pliku 1 koniec pliku 13 Liczba całkowita 14 Stała zmiennoprzecinkowa 15 Stała tekstowa („label”) 16 Formuła z przechowywaną wartością zmiennoprzecinkową Mimo, że nasz program nie próbuje rozszyfrować formuły, to byłoby całkiem możliwe aby to zrobić,

tworząc wyrażenia, które mogą być oceniane przy użyciu wyrażenia „is”. Możemy to zrobić pobierając wartości zmiennoprzecinkowe, które są przechowywane razem z każdą formułą. Podobnie jak wszystkie przykłady w tym rozdziale, LOTUS.PL czyta ze standardowego wejścia. Oczywiście, w dowolnej praktycznej aplikacji, powinien być dostosowany do odczytu z pliku identyfikowanego przez uchwyt.

Page 20: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Język i Metajęzyk Metajęzyk jest językiem używanym do opisania innego języka. W całej tej książce opisujemy Prolog w

języku angielskim; Oznacza to, że używamy języka angielskiego jako metajęzyka dla Prologa. Możemy używać języka angielskiego lub dowolnego języka jako metajęzyk dla każdego języka programowania. Niektóre języki programowania, takie jak Algol, mają specjalne metajęzyki. Prolog jest niemalże wyjątkowy, jednakże w zakresie w jakim może służyć jako własny metajęzyk. Przejawia się to w wielu funkcjach:

– Program może tworzyć nowe funkcje poprzez obliczenia, a następnie wykonując je. W istocie, można użyć Prologa aby opisać konstrukcje funkcji.

– Program może zbadać sam siebie(za pomocą klauzuli) i modyfikować się (używając assert i retract).

– Deklarując operatory, program może sam zmienić składnię języka Prologa

– Program w Prologu może rozszerzyć i zmodyfikować silnik inferencji, który kontroluje wykonywanie programu. Tak więc, język może zmienić sam siebie w sposób , który wykracza poza zewnętrzną składnię.

Funkcje te umożliwiają Prologowi robić rzeczy, które są zupełnie obce dla większości języków programowania. Co najważniejsze, Prolog zaciera różnicę między programem a danymi. W większości języków programowania, trzeba bardzo wyraźnie odróżnić decyzje które podejmujemy podczas pisania programu od decyzji, które podejmuje komputer podczas uruchamiania programu. Na przykład, wszystkie arytmetyczne wyrażenia w programie BASIC lub Pascal są napisane przez programistę z wyprzedzeniem, choć program może zdecydować, w czasie pracy, których z nich użyć. W Prologu program może sam siebie rozszerzyć i zmodyfikować, podczas działania. Z tego powodu często nauka Prologa przychodzi łatwiej początkującym programistom, niż ludziom którzy programowali już w wielu językach programowania i nauczyli się już, że komputer nie pracuje w ten sposób.

Page 21: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Zbieranie alternatywnych rozwiązań do listy

Rozważmy małą bazę danych: father(michael,cathy). father(charles_gordon,michael). father(jim,melody). Możemy zapytać Prologa, aby wyświetlił imiona wszystkich ojców wydając

zapytanie takie jak: ?- father(X,_), write(X), nl, fail. To jest: Znajdź X, dla którego ojciec (X, _) się powiedzie, wypisać, i wycofać

aby znaleźć kolejne. Ale co, jeśli zamiast wyświetlania imion, chcemy je dalej przetwarzać jako

lista? Stoi przed dylematem. W celu uzyskania wszystkich imion, program musi wracać się. Ale w celu skonstruowania listy, musi użyć rekurencji, przechodząc przez częściowo zbudowaną listę od jednej iteracji do następnej – czego program wykorzystujący backtracking nie zrobi.

Page 22: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Zbieranie alternatywnych rozwiązań do listy

Jedną z możliwości byłoby użycie „assert” i „retract” do wdrożenia mniej więcej następującego algorytmu:

1. Wracać się przez wszystkie rozwiązania father (X, _), przechowywując każdą wartość X oddzielnie w bazie danych;

2. Po wypróbowaniu wszystkich rozwiązań, wykonać pętlę rekurencyjną, która cofnie wszystkie zapisane klauzule i zgromadzi informacje na liście.

Na szczęście nie musimy przechodzić przez to wszystko. Wbudowany predykat „findall” zgromadzi rozwiązania kwerendy do listy, bez potrzeby wykonywania „asserts” i „retracts”. Oto przykład:

?- findall(X,father(X,_),L). L = [michael,charles_gordon,jim] Bardziej ogólnie, zapytanie z formularza ?- findall(Variable,Goal,List). Utworzy instancję „List” do listy wszystkich instancji „Variable”, które odnoszą się do

rozwiązań „Goal”. Następnie można przetwarzać tą listę, w jakikolwiek sposób chcemy.

Pierwszym argumentem FindAll nie musi być zmienna; może to być dowolne pojęcie ze zmiennymi. Trzeci argument będzie wówczas listą instancji pierwszego argumentu z której każdy odpowiadać będzie rozwiązaniu. Na przykład:

?- findall(Parent+Child,father(Parent,Child),L). L = [michael+cathy,charles_gordon+michael,jim+melody] Znakiem (+) jest tutaj po prostu funktor wpisany pomiędzy swoimi argumentami.

Page 23: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Używanie „bagof” i „setof”

BAG jest matematycznym obiektem takim jak zbiór z tym wyjątkiem, że ten sam element może się wnim pojawić więcej niż raz. Oczywiście „findall” tworzy „bag”, nie zbiór, ponieważ to samo rozwiązanie może pojawić się w nim więcej niż raz. Podobnie „bagof” tworzy torbę w formie listy zawierającej wszystkie rozwiązania kwerendy; „setof” robi to samo oprócz tego, że lista jest posortowana w kolejności alfabetycznej, a duplikaty są usuwane.

Operacja sortowania wymaga dodatkowego czasu, ale może to być korzystne, ponieważ chroni nas przed duplikatami w późniejszej pracy.

Największa różnica między „bagof” i „setof” z jednej strony, a FindAll z drugiej strony, dotyczy sposobu obsługi zmiennych w „Goals”, któr nie występują w X. Jak można się spodziewać, „findall” traktuje je jako „niewystępujące” na każdym przejściu.

Page 24: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Używanie „bagof” i „setof”

Zatem:

?- findall(X,parent(X,Y),L). oznacza "Znajdź każdego kto jest rodzicem kogokolwiek" – Y

nie musi mieć takiej samej wartości dla każdego z rodziców. Ale

?- bagof(X,parent(X,Y),L). oznacza "Znajdź wszystkie wartości X-a, które wiążą się z jakąś

konkretną wartością Y-a.„ Inne wartości

Y-a przyniosą alternatywne rozwiązania do bagof, a nie dodatkowe wpisy do tej samej listy.

Page 25: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Używanie „bagof” i „setof” Oto przykład: parent(michael,cathy). parent(melody,cathy). parent(greg,stephanie). parent(crystal,stephanie). ?- findall(X,parent(X,Y),L). X = _0001, Y = _0002, L=[michael,melody,greg,crystal] ?- bagof(X,parent(X,Y),L). X = _0001, Y = cathy, L = [michael,melody] ; X = _0001, Y = stephanie, L = [greg,crystal] ?- setof(X,parent(X,Y),L). X = _0001, Y = cathy, L = [melody,michael] ; X = _0001, Y = stephanie, L = [crystal,greg] Oczywiście „setof” jest jak „bagof” z wyjątkiem, że sortuje listy i usuwa duplikaty (jeśli jakiekolwiek

występują). Ale jest inny sposób. Można zrobić to: ?- bagof(X,Y^parent(X,Y),L). X = _0001, Y = _0002, L = [michael,melody,greg,crystal] Poprzedzenie Y^ wskazuje, że Y ma być traktowana jako egzystencjalnie skwantyfikowana.

Page 26: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Znajdowanie najmniejszego, największego, lub „najlepszego” rozwiązania

Dość często, będziemy chcieli znaleźć "najlepsze" z wielu alternatywnych rozwiązań dla zapytania. Oczywiście "najlepsze" oznacza różne rzeczy w różnych sytuacjach, ale podstawową ideą jest to, że ze wszystkich możliwych rozwiązań, chcesz to, które przewyższa wszystkie inne zgodnie z pewnymi kryteriami. Istnieją trzy główne metody:

– Użyć „setof” i wykorzystać wbudowany proces sortowania, tak że "najlepsze" rozwiązanie wychodzi na początku (a może na końcu) listy;

– Użyć „bagof” lub „setof” a następnie pracować z listą , aby wybrać rozwiązanie, którego potrzebujesz;

– Szukaj bezpośrednio „najlepszego” rozwiązania, porównując każdą alternatywę na tle wszystkich pozostałych

Page 27: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Znajdowanie najmniejszego, największego, lub „najlepszego” rozwiązania

Popracujmy z następującą bazą danych: age(cathy,8). age(sharon,4). age(aaron,3). age(stephanie,7). age(danielle,4). Które dziecko jest najmłodsze? Spróbujemy pierwszej i trzeciej strategii, pozostawiając

drugą jako ćwiczenie. Jest to łatwe do wykonania. „setof” daje nam wiek najmłodszego dziecka. Weźmy pod uwagę te zapytania:

?- setof(A,N^age(N,A),L). L = [3,4,7,8] ?- setof(A,N^age(N,A),[Youngest|_]). Youngest = 3 Pierwsza kwerenda pobiera posortowaną listę wieku dzieci; druga kwerenda pobiera

tylko pierwszy element tej listy. (dalszy ciąg przykład str. 23)

Page 28: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Zapytania Intensjonalne i Ekstensjonalne

Wiemy, w jaki sposób w Prologu stwierdzić fakty na temat jednostek oraz uogólnień: dog(fido). “Fido is a dog.” animal(X) :- dog(X). “Dogs are animals.” Wiemy również, jak zadawać pytania dotyczące jednostek: ?- dog(fido). “Is Fido a dog?” Ale w jaki sposób możemy zadać pytanie "Czy psy są zwierzętami?" Istnieją dwa

znaczenia takiego pytania: (1) Czy istnieje reguła lub zbiór zasad za pomocą których możne dowieść, że

wszystkie psy są zwierzętami? (2)Niezależnie od tego co mówią zasady, czy jest to przypadek, że wszystkie psy

wymienione w bazie danych są w rzeczywistości zwierzętami? Możemy nazwać (1) i (2) intensjonalną oraz ekstensjonalną operacją, odpowiednia na

pytanie „Czy psy są zwierzętami?” Spośród nich (1) jest przede wszystkim pytaniem o zawartość zestawu reguł, a (2) pyta Prologa o zrobienie generalizacji zestawu znanych już jednostek. Oczywiście, jeżeli pytanie (1) jest prawdziwe to pytanie (2) również będzie, aczkolwiek konwersja nie jest tutaj przypadkiem do rozważania.

Page 29: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Zapytania Intensjonalne i Ekstensjonalne Będziemy realizować pytanie (2) . Intuicyjnie, chcemy powiedzieć, że "Wszystkie psy są zwierzętami". To prawda, jeśli (a)

istnieje co najmniej jeden pies w bazie danych, oraz (b) nie ma psa w bazie danych, która nie jest zwierzęciem. Wymagamy, że musi być co najmniej jeden pies w bazie danych, tak że, na przykład zdanie, "Ślimaki są małpami" nie wyjdzie jako prawdziwe tylko dlatego, że nie ma ślimaków w bazie danych.

Chcemy zdefiniować predykat for_all(GoalA, GoalB), który się powiedzie, jeśli wszystkie instancję które czynią GoalA prawdziwym, czynią prawdziwym również GoalB. Jeżeli wyniki mają być sensowne, GoalA i GoalB muszą dzielić się co najmniej jedną zmienną. Moglibyśmy wtedy zapytać: "Czy psy zwierzętami?" za pomocą zapytania:

?- for_all(dog(X),animal(X)).

Jeden ze sposobów zdefiniowania „for_all” jest następujący:

% for_all(GoalA,GoalB)

% Powiedzie się, jeśli wszystkie rozwiązania GoalA będą spełniać również rozwiązania GoalB,

% oraz istnieje co najmniej jedno rozwiązanie dla obu funkcji.

for_all(GoalA,GoalB) :-

\+ (call(GoalA), \+ call(GoalB)), % 1

call(GoalA), % 2

!. % 3

Zagnieżdżone negacje w linii 1 mogą być mylące. Linia pierwsza nie powiedzie się jeśli funkcja złożona(compound goal) się powiedzie i vice cersa.

(call(GoalA), \+ call(GoalB))

Z kolei ta złożona funkcja(compound goal) , powiedzie się jeżeli istnieje sposób aby GoalA zadziałał ( tworząc instancję kilku zmiennych współdzielonych w procesie z GoalB ) tak, że następnie GoalB zawiedzie. Jeśli linia 1 się uda, następnie linia 2 sprawdza, czy rzeczywiście istnieje co najmniej jeden pies w baza danych. Nie możemy odwrócić kolejność linii 1 i 2, ponieważ linia 2 tworzy instancje pewnych zmiennych, które muszą być potem usunięte w linii 1. Cięcia w linii 3 zapewniają, że nie generujemy fałszywych alternatyw dokonując poprawnego zaimplementowania linii 2 dowolnymi sposobami.

Page 30: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Definicje operatora

Większość funktorów w Prologu jest zapisana bezpośrednio przed nawiasami argumentu listy: Funktor(arg1, arg2). Funktory, które mogą być napisane w innych pozycjach są nazywane Operatorami. Na przykład, struktura +(2,3) może być zapisana jako 2+3, ponieważ jej funktor, +, Jest operatorem wrostkowym.

Nie należy mylić operatorów z operacjami. Niektóre operatory oznaczają arytmetyczne operacje(+-*/), ale inne operatory służą zupełnie innym celom. W rzeczywistości jakikolwiek funktor w Prologu może być uznany za operator oraz zmieniając w ten sposób tylko jego składnie, nie znaczenie.

Page 31: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Powszechnie predefiniowane operatory w PROLOGu.

Pierwszeństwo Specyfikator Operatory 1200 xfx :- --> 1200 fx :- ?- 1100 xfy ; 1050 xfy -> 1000 xfy , 900 fy \+ (lub w niektorych

Prologach, not) 700 xfx = \= == \== @< @=< @> @>= is =:= =\= <

=< > >= =.. 500 yfx + - 400 yfx * / // mod 200 xfy ^ 200 fy -

Page 32: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Specyfikatory składni operatora

Specyfikator Znaczenie

fx Prefix, not associative

fy Prefix, right-associative (like \+)

xf Postfix, not associative

yf Postfix, left-associative

xfx Infix, not associative (like =)

xfy Infix, right-associative (like the comma in compound goals)

yfx Infix, left-associative (like +)

Page 33: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Nadawanie znaczeń operatorom Definicje operatora określają jedynie jego składnie. Znaczenie lub semantyka operatora zależy już od

programisty. W przeszłości niektóre Prologi używały ampersandu (&) zamiast przecinka aby łączyć elementy funkcji

złożonych . Jest to wielką zaletą, ponieważ złożone funkcje nie wyglądają jak listy argumentów; zapytanie

?- call(p(a) & q(a) & r(a)). wyraźnie wywołuje „call” tylko z jednym argumentem. W zwykłym Prologu, musimy użyć dodatkowych

nawiasów, aby uzyskać ten sam efekt, jak tutaj: ?- call( (p(a), q(a), r(a)) ). ponieważ bez dodatkowych nawiasów, „call” będzie traktowane jako posiadające trzy argumenty. Możemy zdefiniować ampersanda aby pracował w ten sposób nawet w zwykłym Prologu. Najpierw zdefiniujmy jego składnię. Chcemy aby & był operatorem wrostkowym z nieco niższym

priorytetem niż przecinek, dzięki czemu f (& b, c) będzie oznaczać f ((& b), c),a nie f (i (b, c)). Ponadto, jak wkrótce zobaczymy, & powinno być w prawo-asocjacyjne. Tak więc, w większości Prologów stosowną definicją operatora będzie:

:- op(950,xfy,&). Następnie musimy powiedzieć Prologowi jak rozwiązać funkcję zawierającą ampersandy. Oczywiście

zapytanie GoalA & GoalB powinno zadziałać jeżeli GoalA zadziała a następnie GoalB zadziała z tymi samymi instancjami. Czyli:

GoalA & GoalB :- call(GoalA), call(GoalB). To jest po prostu zwykła reguła Prologa. Równie dobrze może to być zapisane jako: '&'(GoalA,GoalB) :- call(GoalA), call(GoalB).

Page 34: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Nadawanie znaczeń operatorom

Ponieważ ampersand & jest asocjacyjny z prawej strony(prawostronny), zasada ta może w rzeczywistości obsługiwać nieograniczoną liczbę zadań połączonych Ampersandami. Załóżmy, że mamy wydać zapytanie:

?- write(one) & write(two) & write(three). Ze względu na „prawostronność” funkcja ta jest równoważna funkcji: ?- write(one) & (write(two) & write(three)). To łączy się z regułą definiującą ampersand „&” z instancjami: GoalA = write(one) GoalB = (write(two) & write(three)) Nowymi funkcjami są call(GoalA), która jest realizowana wypisując „one” na

ekranie, oraz call(GoalB), która rekurencyjnie powołuje się na ręgułę ampersandu „&”.

Page 35: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

PROLOG W PROLOGu Nasza definicja ampersandu „&” sugeruje strategię przepisania całego mechanizmu inferencji Prologa do

Prologa, aby rozwinąć tym samym zmodyfikowaną wersję języka. Przypomnijmy, że predykat clause(Head, Body) może odzyskać jakąkolwiek z klauzul w bazie danych, a przynajmniej te, które są zadeklarowane dynamicznie; Robi to, próbując połączyć Head z nagłówkiem klauzuli oraz body z ciałem klauzuli(lub z „true” jeśli klauzula jest faktem). Klauzule alternatywne są otrzymywane jako wielokrotne rozwiązania „clause” przy pomocy backtrackingu.

Ciało reguły jest zazwyczaj funkcją złożoną(compound goal), czyli struktura utrzymywana jest przy pomocy prawostronnych(right-associative) przecinków, które działają dokładnie jak ampersandy zaprezentowane wcześniej. Tak więc biorąc pod uwagę regułę:

f(X) :- g(X), h(X), i(X), j(X).

zapytanie:

?- clause(f(abc),Body).

Utworzy instancję „Body”

g(abc), h(abc), i(abc), j(abc)

która odpowiada:

g(abc), (h(abc), (i(abc), j(abc)))

Aby wykonać to zadanie postąpimy w ten sam sposób jak zrobiliśmy to w przypadku ampersandów. Możemy zdefniować interpret ( który pobiera funkcję jako argument i wykonuje ją).

Następnie aby użyć „interpret” wystarczy napisać, np.:

?- interpret(grandparent(X,Y)). zamiast ?- grandparent(X,Y).

Jest to algorytm Clocksin'a i Mellish'a (1984:177) . Cięcia są „zielone” : zachowują kroki, ale nie wpływają na logikę programu. Zauważmy, że nie jest tu używane wywołanie; Każda udana funkcja kończy się wywołaniem interpret(true).

Page 36: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Rozszerzenie silnika wnioskowania(inferencji) Mniej radykalnym sposobem na rozszerzenie Prologa jest dodanie kolejnej strategii inferencyjnej na wierzchu istniejącego już silnika wnioskowania. Taki system

nie wymaga meta-interpretatora. Przetwarza on funkcję próbując ją wykonać poleceniem „call”, a następnie próbuje innej strategi jeżeli „call” nie skutkuje. Tak więc typowe funkcje w Prologu są przetwarzane w normalny sposób, zarówno jak i inne ich typy. Na konkretnym przykładzie, zastanówmy się, jak wyrazić w Prologu fakt, że wszystkie psy są z rodziny psowatych oraz, że wszystkimi członkami rodziny psowatych są psy. Jeśli użyjemy dwóch reguł,

canine(X) :- dog(X). dog(X) :- canine(X). otrzymamy pętle, ponieważ każda reguła wywoła inną. Jest tutaj potrzebna inna strategia inferencji. Najpierw musimy zapisać fakt, że canine(x) oraz dog(x) wsą w bikondycjonalnej relacji. Możemy użyć predykatu :bicond” i umieścić nastepujący fakt w bazie

danych: bicond(canine(X),dog(X)). Lecz jest możliwe bardziej eleganckie podejście. Zamiast ”bicond” wywołajmy predykat '-:-‘ (operator podobny do ':-' lecz symetryczny). Do tego potrzebujemy

definicji operatora: :- op(950,xfx,'-:-'). Teraz możemy odnieść się do „dog” oraz „canine” korzystając z faktu: canine(X) -:- dog(X). Nazwiemy to zasadą bikondycjonalną. Żadna z jej stron nie może być funkcją złożoną. Plik BICOND.PL definiuje predykat „prove”, który odpowiada predykatowi „call” tym , że może on również sotsować zasad bikondycjonalnych. Jego strategia jest

nastepująca: najpierw sprawdzić czy funkcja może być wywołana przez „call”. Jeżeli nie, sprawdzić czy jest ona zgodna z jedną ze stron zasady bikondycjonalnej. Jeśli tak, wywołać drugą z tych zasad. Aby sprawdzić, że mamy poprawne wyniki, należy rozważyć następującą bazę danych i zapytania:

dog(fido). canine(rolf). dog(X) -:- canine(X). ?- dog(X). X = fido ?- canine(X). X = rolf ?- prove(dog(X)). X = fido ; X = rolf ?- prove(canine(X)). X = rolf ; X = fido Zapytania z „prove” rozpoznają bikondycjonale, podczas gdy zwykłe zapytania, nie. Bikondycjonale nie powodują pętli ponieważ „prove” nie może sam siebie

wywołać. Niestety „prove” ma swoje ograniczenia. Ponieważ nie jest rekurencyjny, nie rozpoznaje, że bikondycjonale są przechodnie. Baza danych nie umozliwia komputerowi aby wywnioskował f(fido) z h(fido). Jeśli potraktujemy „prove” rekurencja w prosty sposób – tak, że wywoła samą siebie zamiast wywoływać „call” - otrzymamy przechodniość, ale również przywrócimy pętle. Bardziej wyrafinowana wersja „prove” może przechowywać zapis tych bikondycjonalnych zasad których używała, tak, że może ona zespoić zasady ze sobą bez użycia tej samej reguły więcej niż jeden raz.

(przykład 5.12 wyklad)

Page 37: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Dostosowywanie interfejsu użytkownika

Nie tylko silnik inferencji, ale również interfejs najwyższego poziomu Prologa może być spersonalizowany. W typowym najwyższym poziomie Prologa, komputer ustala:

?- a użytkownik odpowiada wpisując zapytanie. Niektóre Prologi pozwalają również

użytkownikowi ustalić fakty i zasady, które są dodane do bazy danych. Plik TOPLEVEL.PL określa najwyższy poziom, którego dialogi z użytkownikiem wyglądają następująco:

Type a query: father(X,cathy). Solution found: father(michael,cathy) Look for another? (Y/N): n Type a query: father(joe,cathy). No (more) solutions Type a query: parent(X,Y). Solution found: parent(michael,cathy) Look for another? (Y/N): y Solution found: parent(melody,cathy) Look for another? (Y/N): y No (more) solutions

Page 38: Odczytywanie danych w różnych formatach - math.uni.lodz.plmath.uni.lodz.pl/~kowalcr/PlockParad/Prolog5.pdfProblem INPUT-a w wolnej formie W tym rozdziale pokażemy, jak sprawić,

Dostosowywanie interfejsu użytkownika Jest to zwykły „najwyższy poziom” w Prologu z tym wyjątkiem, że komunikaty są

znacznie bardziej jawne, a odpowiedzi są podawane poprzez wyświetlanie zapytań z wypełnionymi wartościami zmiennych. Wszystkie zapytania Prologa są dopuszczalne - nie tylko zapytania o bazę danych, ale również wywołania do wbudowanych predykatów takich jak „consult”.

Kod określający ten „najwyższy poziom” ma tylko 18 linii. Procedura top_level jest nieskończoną pętlą repeat-fail , która akceptuje zapytania i przekazuje je do find_solutions. (Zauważ przy okazji, że nazwa top_level nie ma specjalnego znaczenia. Ta procedura staje się najwyższym poziomem środowiska Prologa zaraz po uruchomieniu go, wpisując '?- top_level.')

W find_solutions procedura ma dwie klauzule. Pierwsza klauzula znajduje rozwiązanie, wypisuje je, i pyta użytkownika, czy szukać innych. Jeśli użytkownik wpisze N lub n (na "nie"), find_solutions wykonuje cięcie, i sterowanie powraca do top_level. W przeciwnym razie, find_solutions cofa się. Jeśli nie można znaleźć dalszych rozwiązań i cięcie nie zostało wykonane, sterowanie przechodzi do drugiej klauzuli i wyświetla się komunikat "Brak (dalszych) rozwiązań".

Jak wyjść z top_level? Po prostu wpisz 'halt.' aby wyjść z Prologa, tak jakbyś używał zwykłego „najwyższego poziomu”.

Spersonalizowany „najwyższy poziom” może uczynić Prolog znacznie bardziej przyjaznym dla użytkownika. Przydatnym okazało się przedstawienie Prologa początkującym programistom poprzez interfejs użytkownika kierowanym za pomocą menu, który pozwalał im wyświetlać bazę danych, dodawać lub usuwać klauzule, oraz wykonywać zapytania. Dzięki temu używanie edytora plików jest wyeliminowane. Ponadto spersonalizowany „najwyższy poziom” może być połączony z wzmocnionym silnikiem inferencji, po to aby zamienić Prologa w potężny system inżynierii danych.