3

I need to decompress a list in prolog , like in the example below :

decode([[a,1],[b,2],[c,1],[d,3]],L). 
L = [a, b, b, c, d, d, d] ; 

I made this code :

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]):-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),append(H2,L,L2),decode(T,L2).

and when i call

makelist2([a,3],L2).
L2 = [a,a,a].

but when i call

decode([[a,3],[b,1],[c,4]],L)

runs continuously. What am i doing wrong ?

Toby Allen
  • 10,997
  • 11
  • 73
  • 124

6 Answers6

3

The problem is in the order of your append and decode in the last clause of decode. Try tracing it, or even better, trace it "by hand" to see what happens.

Another approach: see this answer. So, with repeat/3 defined as:

% True when L is a list with N repeats of X
repeat(X, N, L) :-
    length(L, N),
    maplist(=(X), L).

You can write your decode/2 as:

decode([], []).
decode([[X,N]|XNs], Decoded) :-
    decode(XNs, Decoded_rest),
    repeat(X, N, L),
    append(L, Decoded_rest, Decoded).

But this is a slightly roundabout way to do it. You could define a difference-list version of repeat/3, called say repeat/4:

repeat(X, N, Reps, Reps_back) :-
    (   succ(N0, N)
    ->  Reps = [X|Reps0],
        repeat(X, N0, Reps0, Reps_back)
    ;   Reps = Reps_back
    ).

And then you can use a difference-list version of decode/2, decode_1/3

decode(Encoded, Decoded) :-
    decode_1(Encoded, Decoded, []).

decode_1([], Decoded, Decoded).
decode_1([[X,N]|XNs], Decoded, Decoded_back) :-
    repeat(X, N, Decoded, Decoded_rest),
    decode_1(XNs, Decoded_rest, Decoded_back).


?- decode([[a,1],[b,2],[c,1],[d,3]],L).
L = [a, b, b, c, d, d, d].

?- decode([[a,3],[b,1],[c,0],[d,3]],L).
L = [a, a, a, b, d, d, d].

?- decode([[a,3]],L).
L = [a, a, a].

?- decode([],L).
L = [].
Community
  • 1
  • 1
  • Thank you very much , i tested,it works but i am very new in prolog , this code being over my power of understanding the language . May i have a little bit help in understanding what i'm doing wrong in my example ? – Moroianu Alexandru Jan 22 '15 at 09:56
  • @MoroianuAlexandru See the first paragraph of my answer –  Jan 22 '15 at 10:19
  • @MoroianuAlexandru and note that both my first solution and the very nice solution by @capellic do the `append` only after making a proper list, not before that. –  Jan 22 '15 at 10:38
3

Another variation of the theme, using a slightly modified version of Boris' repeat/3 predicate:

% True when L is a list with N repeats of X
repeat([X, N], L) :-
    length(L, N),
    maplist(=(X), L).

decode(Encoded, Decoded) :-
    maplist(repeat, Encoded, Expanded),
    flatten(Expanded, Decoded).

If Encode = [[a,1],[b,2],[c,1],[d,3]], then in the above decode/2, the maplist/3 call will yield Expanded = [[a],[b,b],[c],[d,d,d]], and then the flatten/2 call results in Decoded = [a,b,b,c,d,d,d].

In SWI Prolog, instead of flatten/2, you can use append/2 since you only need a "flattening" at one level.


EDIT: Adding a "bidirectional" version, using a little CLPFD:
rle([], []).
rle([X], [[1,X]]).
rle([X,Y|T], [[1,X]|R]) :-
    X \== Y,     % use dif(X, Y) here, if available
    rle([Y|T], R).
rle([X,X|T], [[N,X]|R]) :-
    N #= N1 + 1,
    rle([X|T], [[N1,X]|R]).

This will yield:

| ?- rle([a,a,a,b,b], L).

L = [[3,a],[2,b]] ? ;

(1 ms) no
| ?- rle(L, [[3,a],[2,b]]).

L = [a,a,a,b,b] ? ;

no
| ?- rle([a,a,a,Y,Y,Z], [X, [N,b],[M,c]]).

M = 1
N = 2
X = [3,a]
Y = b
Z = c ? a

no
| ?- rle([A,B,C], D).

D = [[1,A],[1,B],[1,C]] ? ;

C = B
D = [[1,A],[2,B]] ? ;

B = A
D = [[2,A],[1,C]] ? ;

B = A
C = A
D = [[3,A]] ? ;

(2 ms) no
| ?- rle(A, [B,C]).

A = [D,E]
B = [1,D]
C = [1,E] ? ;

A = [D,E,E]
B = [1,D]
C = [2,E] ? ;

A = [D,E,E,E]
B = [1,D]
C = [3,E] ? ;
...

| ?- rle(A, B).

A = []
B = [] ? ;

A = [C]
B = [[1,C]] ? ;

A = [C,D]
B = [[1,C],[1,D]] ? ;
...

As @mat suggests in his comment, in Prolog implementations that have dif/2, then dif(X,Y) is preferable to X \== Y above.

lurker
  • 56,987
  • 9
  • 69
  • 103
  • 1
    A very good solution. I just have a pathological dislike for `flatten` :) but in this case it allows for a clean solution. –  Jan 22 '15 at 12:29
  • quoting from [the docs](http://www.swi-prolog.org/pldoc/doc_for?object=flatten/2), "Ending up needing flatten/3 often indicates ... a bad design.", but you don't actually need `flatten/2`, only `flatten_one_level/2`, better known as [`concat`](http://hackage.haskell.org/package/base-4.7.0.2/docs/Data-List.html#v:concat) in the FP world. Which is perfectly fine and dandy. – Will Ness Jan 22 '15 at 18:25
  • @WillNess right. I'm used to doing things through GNU Prolog. In SWI I could use `append/2`. – lurker Jan 22 '15 at 18:38
  • 2
    +1 for attempting an omnidirectional solution. In SICStus Prolog, SWI and several others, you can use `dif/2` instead of `(\==)/2` to make it correct. – mat Jan 23 '15 at 12:47
2

You can deal with both direction with this code :

:- use_module(library(lambda)).
% code from Pascal Bourguignon
packRuns([],[]).

packRuns([X],[[X]]).

packRuns([X|Rest],[XRun|Packed]):-
    run(X,Rest,XRun,RRest),
    packRuns(RRest,Packed).


run(Var,[],[Var],[]).

run(Var,[Var|LRest],[Var|VRest],RRest):-
    run(Var,LRest,VRest,RRest).

run(Var,[Other|RRest],[Var],[Other|RRest]):-
    dif(Var,Other).
%end code

pack_1(In, Out) :-
    maplist(\X^Y^(X = [V|_],
              Y = [V, N],
              length(X, N),
              maplist(=(V), X)),
        In, Out).

decode(In, Out) :-
    when((ground(In); ground(Out1)),pack_1(Out1, In)),
    packRuns(Out, Out1).

Output :

 ?- decode([[a,1],[b,2],[c,1],[d,3]],L).

L = [a, b, b, c, d, d, d] .

 ?- decode(L, [a,b,b,c,d,d,d]).

L = [[a, 1], [b, 2], [c, 1], [d, 3]] .
lurker
  • 56,987
  • 9
  • 69
  • 103
joel76
  • 5,565
  • 1
  • 18
  • 22
  • Nice shot at a relational solution (+1). Although, `decode([[a,1],[c,0],[b,3]], L).` fails. But for a relational solution, perhaps expecting the counts to be `> 0` makes sense. – lurker Jan 22 '15 at 20:38
1

a compact way:

decode(L,D) :- foldl(expand,L,[],D).
expand([S,N],L,E) :- findall(S,between(1,N,_),T), append(L,T,E).

findall/3 it's the 'old fashioned' Prolog list comprehension facility

CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • 1
    Great answer as always. I am not sure, but can you use the four-argument `findall/4` to avoid the `append` in `expand/3` completely? –  Jan 22 '15 at 10:23
  • 2
    @Boris: actually, I tried, but it gives the reversed answer: `expand([S,N],L,E) :- findall(S,between(1,N,_),E,L).` – CapelliC Jan 22 '15 at 10:26
1

decode is a poor name for your predicate: properly done, you predicate should be bi-directional — if you say

decode( [[a,1],[b,2],[c,3]] , L )

You should get

L = [a,b,b,c,c,c].

And if you say

decode( L , [a,b,b,c,c,c] ) .

You should get

L = [[a,1],[b,2],[c,3]].

So I'd use a different name, something like run_length_encoding/2. I might also not use a list to represent individual run lengths as [a,1] is this prolog term: .(a,.(1,[]). Just use a simple term with arity 2 — myself, I like using :/2 since it's defined as an infix operator, so you can simply say a:1.

Try this on for size:

run_length_encoding( []     , []     ) .  % the run-length encoding of the empty list is the empty list.
run_length_encoding( [X|Xs] , [R|Rs] ) :- % the run-length encoding of a non-empty list is computed by
  rle( Xs , X:1 , T , R ) ,               % - run-length encoding the prefix of the list
  run_length_encoding( T , Rs )           % - and recursively run-length encoding the remainder
  .                                       % Easy!

rle( []     , C:N , []     , C:N ) .  % - the run is complete when the list is exhausted.
rle( [X|Xs] , C:N , [X|Xs] , C:N ) :- % - the run is complete,
  X \= C                              % - when we encounter a break
  .                                   %
rle( [X|Xs] , X:N , T      , R   ) :- % - the run continues if we haven't seen a break, so....
  N1 is N+1 ,                         % - increment the run length,
  rle( Xs, X:N1, T, R )               % - and recurse down.
  .                                   % Easy!
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • 1
    `run_length_encoding([a,b,b,c,c,c], L).` yields `L = [a:1,b:2,c:3]`, but `run_length_encoding(L, [a:1,b:2,c:3] ).` causes a stack overflow. – lurker Jan 23 '15 at 15:49
1

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).

lurker
  • 56,987
  • 9
  • 69
  • 103