Błędy, które rujnują kod w Prologu
Cztery grzechy główne w programowaniu w Prologu
Prolog ma w sobie coś pociągającego. Podczas gdy większość programistów wybiera paradygmaty imperatywne i obiektowe, entuzjaści Prologu idą swoją drogą. Piszą deklaratywnie, traktując programowanie jako definiowanie zależności zamiast sekwencji instrukcji. To satysfakcjonujące podejście — dopóki nie popełni się kilku typowych błędów.
W praktyce wystarczy unikać czterech pułapek, żeby kod działał poprawnie i był łatwy do utrzymania.
Zbyt wczesne cięcie możliwości
Wielu programistów sięga po operator cięcia (!/0), konstrukcje warunkowe ((->)/2) czy predykaty sprawdzające typy, takie jak var/1. Dają one szybkie efekty przy konkretnych zapytaniach, ale niszczą ogólność relacji.
% Niezalecane: użycie cięcia
factorial(0, 1) :- !.
factorial(N, F) :-
N > 0,
N1 is N - 1,
factorial(N1, F1),
F is N * F1.
Po zapytaniu ?- factorial(N, F). otrzymujemy tylko jedną parę wartości. Pozostałe rozwiązania znikają, bo cięcie blokuje dalsze przeszukiwanie. Zamiast tego warto sięgnąć po dif/2 oraz predykaty wyższego rzędu, które zachowują deklaratywny charakter kodu.
Modyfikacja bazy wiedzy w trakcie działania
Czasem programiści odkrywają assertz/1 i retract/1 i poczuć się, jakby zyskali pełną kontrolę. W rzeczywistości jednak modyfikacja globalnej bazy danych tworzy ukryte zależności.
Jeśli predykaty są wykonywane w innej kolejności niż podczas testów, wyniki mogą się pojawić nagle i zniknąć równie szybko. Testowanie staje się problematyczne, ponieważ każdy przebieg może pozostawić ślady w bazie.
Lepsze podejście polega na przekazywaniu stanu jawnie poprzez argumenty predykatów. W ten sposób zależności są widoczne w kodzie i nie trzeba w nich polegać na magicznych efektach ubocznych.
Mieszanie logiki z efektami ubocznymi
% Niezalecane: mieszanie obliczeń i wyświetlania
solve_and_print :-
solution(S),
format("The solution is: ~q~n", [S]).
Taki predykat nie może być łatwo testowany, bo jego wynik nie jest danymi — jest tekstem na ekranie. Nie można też go użyć jako części większej relacji.
Rozdzielenie warstw jest proste: predykat powinien opisywać rozwiązanie, a wyświetlanie powinno odbywać się na poziomie wywołania top-level. Jeśli potrzebne jest specjalne formatowanie, można wykorzystać DCG.
Ignorowanie nowoczesnych narzędzi
Prolog zmienił się od lat 90. Constraint Logic Programming over Finite Domains (CLP(FD)) jest już od dekad dostępny w większości systemów. Jednak wielu programistów wciąż uży<|eos|>