prologdifference-lists

How to use difference lists in recursion?


The intent of the following program is as follows clist_d(f(a,f(a,a)),R) the result is a list of all ground arguments, e.g R = [a,a,a]

%Difference list append.
diffapp(X-Y,Y-Z,X-Z). 

%2 base case
clist_d(a,[a]).
clist_d(b,[b]).
clist_d(c,[c]).

%If we get a term f(X,Y) the first term of such list is the element in X,followed by the elements in Y
clist_d(f(X,Y),R) :-
    clist_d(X,R1),
    clist_d(Y,R2),
    diffapp([R1|H]-H,R2-[],R-[]).

%We can also get a g
clist_d(g(X,Y),R) :-
    clist_d(X,R1),
    clist_d(Y,R2),
    diffapp([R1|H]-H,R2-[],R-[]).

However, the program contains a bug. Running the program with the following query:

?- clist_d(f(a,a),R).
R = [[a],a] ?

Produces a bug as you can see above, testing the difference list alone I get the following result

?- X = [a,b,c|T], Y = [1,2,3],diffapp(X-T,Y-[],R-[]).
X = [a,b,c,1,2,3],
T = [1,2,3],
Y = [1,2,3],
R = [a,b,c,1,2,3] ? 
yes

I have made a mistake in my main program, but I can't figure out what to add to make my diffapp work there.


Solution

  • With difference lists, you want to pass along a pair of arguments everywhere, and you do need any append, so the top-level call should be:

    clist_d(f(a,f(a,a)),R,[])
    

    The problem with the nesting in the output is because your base cases return a list (R1), and then in the call to append you cons it ([R1|H]). With no append, that does not happen.

    clist_d(f(X,Y),R0,R) :-
      clist_d(X,R0,R1),
      clist_d(Y,R1,R).
    
    clist_d(g(X,Y),R0,R) :-
      clist_d(X,R0,R1),
      clist_d(Y,R1,R).
    
    clist_d(a,[a|R],R).
    clist_d(b,[b|R],R).
    clist_d(c,[c|R],R).
    

    Produces:

    | ?- clist_d(f(a,f(a,a)),R,[]).
    R = [a,a,a] ? ;
    no