prologdifference-lists

Find last item in a difference list


I'm trying to find the last item (all numbers) without "ruining" the list.

What I currently have is this:

max([_|Xs]-Xs,R):-
Xs \= [],
max2(Xs,R),!.
max([X|Xs]-Xs,R):-
    R = X.
max([]-[],99999).

Max2 is a function that finds the last item in an ordinary list:

max2([X],X):-
    number(X),!.
max2([_|Xs],R):-
    max2(Xs,R),!.

When I try on a list with one item, it works - otherwise fails:

max([22|X]-X,R)
R = 22

max([22,27|X]-X,R)
Stack limit (0.2Gb) exceeded
  Stack sizes: local: 0.2Gb, global: 21Kb, trail: 3Kb
  Stack depth: 1,560,328, last-call: 0%, Choice points: 1,560,312
  Probable infinite recursion (cycle):
    [1,560,328] max2([cyclic list], _1452)
    [1,560,327] max2([cyclic list], _1484)

I tried other ways, but then when I gave it a list with one item, it converted it:

max([22|X]-X,R)
X = []
R = 22

So I couldn't keep using the X as a free variable.

I hope I wrote it clearly. Many thanks in advance.


Solution

  • In the Logtalk library, I use the following definition:

    last(List-Back, Last) :-
        List \== Back,
        List = [Head| Tail],
        last(Tail-Back, Head, Last).
    
    last(List, Last, Last) :-
        unify_with_occurs_check(List, Back-Back).
    last(List-Back, _, Last) :-
        List \== Back,
        List = [Head| Tail],
        last(Tail-Back, Head, Last).
    

    Sample calls:

    ?- last([22|X]-X,R).
    R = 22 ;
    false.
    
    ?- last([a,b,c|X]-X,R).
    R = c ;
    false.
    
    ?- last(DL, Last).
    DL = [Last|_1648]-_1648 ;
    DL = [_1652, Last|_1648]-_1648 ;
    DL = [_1652, _1664, Last|_1648]-_1648 ;
    DL = [_1652, _1664, _1676, Last|_1648]-_1648 ;
    DL = [_1652, _1664, _1676, _1688, Last|_1648]-_1648
    ...
    

    This definition is quite general as it also works with lists of variables. For example:

    ?- last([X,Y,Z|Tail]-Tail, Last).
    Z = Last ;
    false.
    

    In your case, if you can ensure that the difference lists are always well formed and their elements are ground, you can simplify the definition.