Spróbujmy rozwiązać prosty problem za pomocą if_/3
; na przykład spróbuję podzielić listę (posortowaną według predykatu p/2
) na dwie listy: prefiks, w którym dla każdego elementu X
mamy p(X, true)
i resztę (w którym, jeśli lista została posortowana na p/2
, musielibyśmy p(X, false)
będę korzystać z biblioteki reif
jak here tak, tu jest kompletny kod mojego programu:..
:- use_module(reif).
pred_prefix(Pred_1, List, L_true, L_false) :-
pred_prefix_aux(List, Pred_1, L_true, L_false).
pred_prefix_aux([], _, [], []).
pred_prefix_aux([X|Xs], Pred_1, True, False) :-
if_( call(Pred_1, X),
( True = [X|True0],
pred_prefix_aux(Xs, Pred_1, True0, False)
),
( True = [],
False = [X|Xs]
)
).
przekazany do tej meta-orzecznika zajmie dwa argumenty predykatu: the pierwszy to bieżący element listy, a drugi to true
lub false
. Idealnie, ten predykat zawsze się powiedzie i nie pozostawi za sobą punktów wyboru.
W pierwszym argumencie z if_/2
predykat jest oceniany za pomocą bieżącego elementu listy; drugim argumentem jest to, co dzieje się, gdy true
; trzecim argumentem jest to, co dzieje się, gdy false
.
Dzięki temu mogę podzielić listę w prowadzeniu a
s i reszta:
?- pred_prefix([X, B]>>(=(a, X, B)), [a,a,b], T, F).
T = [a, a],
F = [b].
?- pred_prefix([X, B]>>(=(a, X, B)), [b,c,d], T, F).
T = [],
F = [b, c, d].
?- pred_prefix([X, B]>>(=(a, X, B)), [b,a], T, F).
T = [],
F = [b, a].
?- pred_prefix([X, B]>>(=(a, X, B)), List, T, F).
List = T, T = F, F = [] ;
List = T, T = [a],
F = [] ;
List = T, T = [a, a],
F = [] ;
List = T, T = [a, a, a],
F = [] .
Jak można pozbyć się wiodącym 0 dla przykładu:
?- pred_prefix([X, B]>>(=(0, X, B)), [0,0,1,2,0,3], _, F).
F = [1, 2, 0, 3].
Oczywiście, może to zostały napisane znacznie prostsze:
drop_leading_zeros([], []).
drop_leading_zeros([X|Xs], Rest) :-
if_(=(0, X), drop_leading_zeros(Xs, Rest), [X|Xs] = Rest).
Tutaj właśnie usunąłem wszystkie niepotrzebne arg .
Jeśli trzeba by zrobić to bezif_/3
, byś musiał napisać:
drop_leading_zeros_a([], []).
drop_leading_zeros_a([X|Xs], Rest) :-
=(0, X, T),
( T == true -> drop_leading_zeros_a(Xs, Rest)
; T == false -> [X|Xs] = Rest
).
Tutaj zakładamy, że =/3
rzeczywiście zawsze uda bez punktów wyboru i T
zawsze będzie albo true
lub false
.
A jeśli nie mamy =/3
albo, można napisać:
drop_leading_zeros_full([], []).
drop_leading_zeros_full([X|Xs], Rest) :-
( X == 0 -> T = true
; X \= 0 -> T = false
; T = true, X = 0
; T = false, dif(0, X)
),
( T == true -> drop_leading_zeros_full(Xs, Rest)
; T == false -> [X|Xs] = Rest
).
który nie jest idealny. Ale teraz przynajmniej możesz sam zobaczyć, w jednym miejscu, co się właściwie dzieje.
PS: Przeczytaj uważnie kod i interakcje na najwyższym poziomie.
Musisz być ekspertem w więcej niż Prolog, aby to zrozumieć ;-) Nazwa tajemnicza też nie pomaga. Należy pamiętać, że pierwszy argument nie może być po prostu niczym: musi to być predykat, który sukcesywnie rozstrzygnie się i zintegruje swój drugi argument z "prawdziwym" lub "fałszem" ("reifikacja"), w przeciwnym razie zostanie zgłoszony błąd. –
PS: Nie mówię, że to nie jest użyteczny predykat, ale jest w tym coś więcej, niż na pierwszy rzut oka. Ukrywanie brzydoty pod warstwami pośrednimi jest wspólną cechą stylu programowania Prolog, promowaną przez "kilku głównych autorów", o których mówisz. Mam nadzieję, że nie uważam się za zbyt negatywny, myślę, że dobrze jest mieć takie konstrukcje i próbować zastosować je do istniejących i nowych problemów. –
PPS: W szczególności przestudiuj definicję '=/3', tuż poniżej' if_/3', [z twojego własnego linku] (http://stackoverflow.com/a/27358600/1812457), gdzie zobaczysz co wystarczy napisać predykat, który dobrze pasuje do 'if_/3'. –