In direct answer to the original question of, What am I doing wrong?...
When I ran the original code, any expected use case "ran indefinitely" without yielding a result.
Reading through the main predicate:
decode([],[]).
This says that []
is the result of decoding []
. Sounds right.
decode([H|T],L) :- makelist2(H,H2), append(H2,L,L2), decode(T,L2).
This says that L
is the result of decoding [H|T]
if H2
is an expansion of H
(which is what makelist2
does... perhaps - we'll go over that below), and H2
appended to this result gives another list L2
which is the decoded form of the original tail T
. That doesn't sound correct. If I decode [H|T]
, I should (1) expand H
, (2) decode T
giving L2
, then (3) append H
to L2
giving L
.
So the corrected second clause is:
decode([H|T], L) :- makelist2(H, H2), decode(T, L2), append(H2, L2, L).
Note the argument order of append/3
and that the call occurs after the decode
of the tail. As Boris pointed out previously, the incorrect order of append
and the recursive decode
can cause the continuous running without any output as append
with more uninstantiated arguments generates a large number of unneeded possibilities before decode
can succeed.
But now the result is:
| ?- decode([[a,3]], L).
L = [a,a,a] ? ;
L = [a,a,a,a] ? ;
...
If you try out our other predicates by hand in the Prolog interpreter, you'll find that makelist2/2
has an issue:
It produces the correct result, but also a bunch of incorrect results. Let's have a look at makelist2/2
. We can try this predicate by itself and see what happens:
| ?- makelist2([a,3], L).
L = [a,a,a] ? ;
L = [a,a,a,a] ? ;
...
There's an issue: makelist2/2
should only give the first solution, but it keeps going, giving incorrect solutions. Let's look closer at makelist/2
:
makelist2(L,L2) :- divide2(L,X,Y), makelist(X,Y,L2).
It takes a list L
of the form [A,N]
, divides it (via divide2/3
) into X = A
and Y = N
, then calls an auxiliary, makelist(X, Y, L2)
.
makelist(_,N,[]):- N =< 0 .
makelist(X,Y,[X|Result]):-Y1 is Y-1,makelist(X,Y1,Result).
makelist/3
is supposed to generate a list (the third argument) by replicating the first argument the number of times given in the second argument. The second, recursive clause appears to be OK, but has one important flaw: it will succeed even if the value of Y
is less than or equal to 0
. Therefore, even though a correct solution is found, it keeps succeeding on incorrect solutions because the base case allows the count to be =< 0
:
| ?- makelist(a,2,L).
L = [a,a] ? ;
L = [a,a,a] ? ;
We can fix makelist/2
as follows:
makelist(_,N,[]):- N =< 0 .
makelist(X,Y,[X|Result]):- Y > 0, Y1 is Y-1, makelist(X,Y1,Result).
Now the code will generate a correct result. We just needed to fix the second clause of decode/2
, and the second clause of makelist/3
.
| ?- decode([[a,3],[b,4]], L).
L = [a,a,a,b,b,b,b]
yes
The complete, original code with just these couple of corrections looks like this:
divide(L, X, Y) :- length(X, 1), append(X, Y, L).
divide2(L, X, Y) :- divide(L, [X|_], [Y|_]).
makelist(_, N, []) :- N =< 0 .
makelist(X, Y, [X|Result]) :- Y > 0, Y1 is Y-1, makelist(X,Y1,Result).
makelist2(L, L2) :- divide2(L, X, Y), makelist(X, Y, L2).
decode([], []).
decode([H|T], L) :- makelist2(H,H2), decode(T,L2), append(H2,L2,L).
Note some simple, direct improvements. The predicate, divide2(L, X, Y)
takes a list L
of two elements and yields each, individual element, X
and Y
. This predicate is unnecessary because, in Prolog, you can obtain these elements by simple unification: L = [X, Y]
. You can try this right in the Prolog interpreter:
| ?- L = [a,3], L = [X,Y].
L = [a,3]
X = a
Y = 3
yes
We can then completely remove the divide/3
and divide2/3
predicates, and replace a call to divide2(L, X, Y)
with L = [X,Y]
and reduce makelist2/2
to:
makelist2(L, L2) :- L = [X, Y], makelist(X, Y, L2).
Or more simply (because we can do the unification right in the head of the clause):
makelist2([X,Y], L2) :- makelist(X, Y, L2).
You could just remove makelist2/2
and call makelist/2
directly from decode/2
by unifying H
directly with its two elements, [X, N]
. So the original code simplifies to:
makelist(_, N, []) :- N =< 0 .
makelist(X, Y, [X|Result]) :- Y > 0, Y1 is Y-1, makelist(X,Y1,Result).
decode([], []).
decode([[X,N]|T], L) :- makelist(X, N, H2), decode(T, L2), append(H2, L2, L).
And makelist/3
can be performed a bit more clearly using one of the methods provided in the other answers (e.g., see Boris' repeat/3
predicate).