Let's try to solve a simple problem using if_/3; for example, I will try to partition a list (sorted on a predicate p/2) in two lists: a prefix in which, for every element X, we have p(X, true), and a rest (in which, if the list was sorted on p/2, we would have p(X, false).
I will use the library reif as here. So, here is the complete code of my program:
:- 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]
)
).
The predicate passed to this meta-predicate will take two arguments: the first is the current list element, and the second will be either true or false. Ideally, this predicate will always succeed and not leave behind choice points.
In the first argument of if_/2, the predicate is evaluated with the current list element; the second argument is what happens when true; the third argument is what happens when false.
With this, I can split a list in leading as and a rest:
?- 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 = [] .
How can you get rid of leading 0's for example:
?- pred_prefix([X, B]>>(=(0, X, B)), [0,0,1,2,0,3], _, F).
F = [1, 2, 0, 3].
Of course, this could have been written much simpler:
drop_leading_zeros([], []).
drop_leading_zeros([X|Xs], Rest) :-
if_(=(0, X), drop_leading_zeros(Xs, Rest), [X|Xs] = Rest).
Here I have just removed all unnecessary arguments.
If you would have to do this without if_/3, you would have had to write:
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
).
Here, we assume that =/3 will indeed always succeed without choice points and the T will always be either true or false.
And, if we didn't have =/3 either, you'd write:
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
).
which is not ideal. But now at least you can see for yourself, in one single place, what is actually going on.
PS: Please read the code and the top level interaction carefully.