Proces tworzenia prostych prologowych aplikacji przebiega zazwyczaj podobnie. Zaczynamy od stworzenia bazy wiedzy, definiujemy niezbędne predykaty (niekiedy również w tym samym pliku), a następnie odpalamy interpreter i pytamy o fakty oraz łączące je relacje. Czy oznacza to, że zdefiniowana na samym początku baza jest czymś niemodyfikowalnym z punktu widzenia programu? Czy możemy zmieniać fakty przy pomocy predykatów?
Spis treści
Predykaty dynamiczne
Okazuje się, że jak najbardziej jest to możliwe, choć jest to funkcjonalność, z której należy korzystać z dużą rozwagą. Zacznijmy od tego, że nie wszystkie predykaty mogą być dynamicznie modyfikowane. Aby było to wykonalne trzeba zastosować wobec nich dyrektywę dynamic/1
. Spójrzmy na przykładową bazę wiedzy, zawierającą fakty dotyczące modelów samochodów:
1 2 |
car_model(ford, t). car_model(ford, fusion). |
Gdybyśmy po wczytaniu tak wyglądającego pliku chcieli zmodyfikować fakty car_model
, niechybnie zostałby wyrzucony wyjątek.W GNU Prologu wyglądałby on tak:
1 |
uncaught exception: error(permission_error(modify,static_procedure,car_model/2),asserta/1) |
Musimy zatem umieścić klauzulę dynamic/1
w którejś z linii poprzedzających definicję pierwszego predykatu car_model
. Na przykład w taki sposób:
1 2 3 |
:- dynamic(car_model/2). car_model(ford, t). car_model(ford, fusion). |
Oznacza to, że uczyniliśmy nasz predykat dynamicznym (domyślnie każdy zdefiniowany przez użytkownika predykat jest statyczny) i możemy przystąpić do dynamicznych operacji na nim.
Definicje
Predykaty, jakich użyjemy to:
- asserta/1 – dodaje klauzulę (fakt lub regułę) do bazy, wstawiając ją na początku
- assertz/1 – wstawia klauzulę na końcu bazy
- retract/1 – usuwa z bazy zunifikowany term
- retractall/1 – usuwa z bazy wszystkie fakty klauzule, których głowa unifikuje się z przekazanym argumentem
Oprócz tego użyjemy pomocniczo następujących predykatów:
- listing/0 – wyświetla aktualny stan bazy (listuje wszystkie zdefiniowane predykaty)
- tell/1 – otwiera plik, aby zapisywać w nim dane
- told/0 – zamyka otwarty wcześniej plik
Przykłady
Powyżej opisanych predykatów możemy używać zarówno z poziomu interaktywnego interpretera, jak również w ciele tworzonych reguł. Możliwe będzie zatem stworzenie na przykład takich predykatów:
1 2 |
add_car_model(Manufacturer, Model) :- asserta(car_model(Manufacturer, Model)). remove_car_model(Manufacturer, Model) :- retract(car_model(Manufacturer, Model)), !. |
Ich wywołanie (np. add_car_model(renault, espace)
albo remove_car_model(ford, fusion)
) zmodyfikuje wczytaną bazę wiedzy, ale po zakończeniu sesji Prologa, zmiany nie będą nigdzie widoczne. Plik z naszymi faktami nie jest bowiem automatycznie synchronizowany z wczytaną do pamięci bazą.
Oznacza to, że chcąc zapisać zmodyfikowaną bazę do pliku (tego samego lub innego), musimy skorzystać z trzech dodatkowych predykatów, wywołanych dokładnie w tej kolejności, jak w poniższym fragmencie:
1 |
tell('cars.pl'), listing, told. |
Kiedy używać
Dynamiczne modyfikowanie bazy wiedzy to z pewnością bardzo ciekawa funkcjonalność. Wynika ona z faktu, że Prolog jest językiem homoikonicznym, to znaczy takim, w którym „reprezentacja programu jest jednocześnie podstawową strukturą danych wykorzystywaną w języku”.
Na początku wpisu wspomniałem jednak o tym, że funkcjonalności tej należy używać z pewną ostrożnością. Zastanówmy się w jakich przypadkach jest to uzasadnione, a w jakich lepiej skorzystać z innych rozwiązań.
Przede wszystkim błędem jest posługiwanie się dynamicznym modyfikowaniem bazy wiedzy w celu symulowania zmiennych globalnych lub przechowywania stanu. Właściwe podejście do tych zagadnień wygląda w Prologu nieco inaczej i powinno być realizowane przy pomocy argumentów predykatów.
Jeśli zaś motywem skłaniającym nas do używania assertów
i retractów
jest optymalizacja wydajności, na przykład w postaci memoizacji (cache’owania obliczonych już wartości, aby nie liczyć ich później ponownie, lecz sięgać po nie do pamięci), to wtedy naruszenie prologowej deklaratywności wydaje się być uzasadnione.
Linki
- Learn Prolog Now! – Database Manipulation.
- SWI Prolog – Database.
- The Power of Prolog – Global Variables in Prolog.
- The Prolog Dictionary – retract, retractall.
- A Declarative Alternative to “assert” in Logic Programming, S. Dietzen, F. Pfenning.
- StackOverflow: How to avoid using assert and retractall in Prolog to implement global (or state) variables.