Istnieje kilka bardzo dobrych odpowiedzi. Spróbuję przyczynić się do dyskusji.
Na temat deklaratywnego programowania logicznego w Prologu znajduje się świetna książka „Craft of Prolog” Richarda O'Keefe . Chodzi o pisanie wydajnych programów przy użyciu języka programowania, który pozwala pisać bardzo nieefektywne programy. W tej książce, omawiając efektywne implementacje kilku algorytmów (w rozdziale „Metody programowania”), autor przyjmuje następujące podejście:
- zdefiniuj problem po angielsku
- napisz działające rozwiązanie, które będzie jak najbardziej deklaratywne; zazwyczaj oznacza to dokładnie to, co masz w swoim pytaniu, po prostu popraw Prolog
- stamtąd podejmij kroki, aby ulepszyć implementację, aby przyspieszyć
Najbardziej pouczająca (jak dla mnie) obserwacja, jaką udało mi się zrobić, przechodząc przez te:
Tak, ostateczna wersja implementacji jest znacznie wydajniejsza niż specyfikacja „deklaratywna”, od której zaczął autor. Jest nadal bardzo deklaratywny, zwięzły i łatwy do zrozumienia. To, co wydarzyło się w międzyczasie, polega na tym, że ostateczne rozwiązanie ujmuje właściwości problemu, dla którego pierwotne rozwiązanie było nieświadome.
Innymi słowy, wdrażając rozwiązanie, wykorzystaliśmy jak najwięcej naszej wiedzy na temat problemu. Porównać:
Znajdź permutację listy, tak aby wszystkie elementy były w porządku rosnącym
do:
Scalenie dwóch posortowanych list spowoduje posortowanie listy. Ponieważ mogą istnieć podlisty, które są już posortowane, użyj ich jako punktu wyjścia zamiast podlist o długości 1.
Odkładając na bok: definicja taka jak ta, którą podałeś, jest atrakcyjna, ponieważ jest bardzo ogólna. Nie mogę jednak uniknąć wrażenia, że celowo ignoruje fakt, że permutacje są, no cóż, problemem kombinatorycznym. To już wiemy ! To nie jest krytyka, tylko spostrzeżenie.
Jeśli chodzi o prawdziwe pytanie: jak iść do przodu? Jednym ze sposobów jest dostarczenie tak dużej wiedzy na temat problemu, który zgłaszamy komputerowi.
Najlepsza znana mi próba rozwiązania tego problemu została przedstawiona w książkach współautorów Aleksandra Stepanowa, „Elementach programowania” i „Od matematyki do programowania ogólnego” . Niestety nie jestem w stanie streścić (a nawet w pełni zrozumieć) wszystkiego w tych książkach. Istnieje jednak podejście polegające na zdefiniowaniu wydajnych (lub nawet optymalnych) algorytmów bibliotecznych i struktur danych, pod warunkiem, że wszystkie odpowiednie właściwości danych wejściowych są znane z góry. Ostateczny wynik to:
- Każda dobrze zdefiniowana transformacja jest udoskonaleniem istniejących już ograniczeń (właściwości, które są znane);
- Pozwalamy komputerowi decydować, która transformacja jest optymalna na podstawie istniejących ograniczeń.
Jeśli chodzi o to, dlaczego jeszcze się to nie wydarzyło, cóż, informatyka jest naprawdę młodą dziedziną, a my wciąż sobie radzimy, naprawdę doceniając nowość większości z nich.
PS
Aby posmakować tego, co mam na myśli przez „dopracowanie implementacji”: weźmy na przykład prosty problem uzyskania ostatniego elementu listy, w Prologu. Kanonicznym rozwiązaniem deklaratywnym jest powiedzenie:
last(List, Last) :-
append(_, [Last], List).
Tutaj deklaratywnym znaczeniem append/3
jest:
List1AndList2
jest konkatenacją List1
iList2
Ponieważ w drugim argumencie append/3
mamy listę zawierającą tylko jeden element, a pierwszy argument jest ignorowany (podkreślenie), otrzymujemy podział pierwotnej listy, który odrzuca przód listy ( List1
w kontekście append/3
) i żąda, aby back ( List2
w kontekście append/3
) jest rzeczywiście listą zawierającą tylko jeden element: więc jest to ostatni element.
Faktyczna realizacja zapewnia SWI-Prolog jednak mówi:
last([X|Xs], Last) :-
last_(Xs, X, Last).
last_([], Last, Last).
last_([X|Xs], _, Last) :-
last_(Xs, X, Last).
To wciąż jest bardzo deklaratywne. Czytaj od góry do dołu, mówi:
Ostatni element listy ma sens tylko dla listy przynajmniej jednego elementu. Ostatnim elementem dla pary ogona i głowy listy jest zatem: głowa, gdy ogon jest pusty, lub ostatni niepusty ogon.
Powodem, dla którego ta implementacja jest zapewniona, jest obejście praktycznych problemów związanych z modelem wykonania Prologu. Idealnie nie powinno mieć znaczenia, która implementacja jest używana. Podobnie moglibyśmy powiedzieć:
last(List, Last) :-
reverse(List, [Last|_]).
Ostatni element listy jest pierwszym elementem listy odwróconej.
Jeśli chcesz wypełnić niejednoznaczne dyskusje na temat tego, co jest dobre, deklaratywne Prolog, po prostu przejrzyj niektóre pytania i odpowiedzi w tagu Prolog na Stack Overflow .