The answer by @Boris does not terminate when the length of the list of the first argument is not known. To see this, there is no need to look any further than the first goal with a failure-slice:
div(L, L1, L2, L3) :-
length(L, Len), false,
% here you compute for example Len1 and Len2
length(L1, Len1),
length(L2, Len2),
append(L1, L1_suffix, L),
append(L2, L3, L1_suffix).
On the other hand, your original program had quite nice termination properties. cTI gave the following optimal termination property:
div(A,B,C,D) terminates_if b(A);b(B);b(C);b(D).
In other words, to ensure termination, you only need a single argument (either A
or B
or C
or D
) to be a concrete list that is finite and ground (that's what b(..)
means). That is a very strong termination condition. It's really a pity that the arguments do not fit! Why not generalize your program? The only problem it has is that it restricts the list elements. So I will replace all variable names of list elements by _
s:
gdiv([], [], [], []).
gdiv([_], [_], [], []).
gdiv([_,_], [_], [_], []).
gdiv([_,_,_|End], [_|XEnd], [_|YEnd], [_|ZEnd]):-
gdiv(End, XEnd, YEnd, ZEnd).
The very same termination properties hold for this program.
Alas, it is now a bit too general. Boris's solution can now be repurposed:
divnew(Zs, As, Bs, Cs) :-
gdiv(Zs, As, Bs, Cs),
append(As, BsCs, Zs),
append(Bs, Cs, BsCs).
My preferred way to express the same would rather be:
divnew(Zs, As, Bs, Cs) :-
gdiv(Zs, As, Bs, Cs),
phrase( ( seq(As), seq(Bs), seq(Cs) ), Zs).
See other answers for a definition of seq//1
.