user3295806
user3295806

Reputation: 41

Prolog: Checking if something is the last item in the list?

I am new to prolog and am basically trying to write a clause that would evaluate as true if a given item is the last item in a given list. Here is what I have:

last(X,[Y|X]).
last(X,[Y|Z]) :- last(X,Z).

I thought this would do the trick, but when I ask prolog:

?- last(c,[a,b,c]).

Prolog returns false. I tried the following query to see what Prolog thinks should fit my definition of last:

?- last(c,X).
X = [_G530|c] ;
X = [_G530, _G533|c] ;
X = [_G530, _G533, _G536|c]

So, what I am not sure about is why the "|" symbol is still in the list?

Update: last([c],[a,b,c]) produces the desired behavior. However, I'm not sure why my 1st argument has to be a list?

Upvotes: 4

Views: 39395

Answers (5)

praveen bhawantha
praveen bhawantha

Reputation: 21

Pic of Question

(a) using concatenation,

conc(_,[Item],List)

(b) Using without concatenation,

Last([Item],[Item]) :- Last(Item,[H|T]) , Last(Item,T)

Upvotes: 0

Dylon Dickinson
Dylon Dickinson

Reputation: 31

Why not just use append?

last(X,Y) :-
    append(_,[X],Y).

http://www.swi-prolog.org/pldoc/man?predicate=append/3

Upvotes: 3

false
false

Reputation: 10102

Why not view things from a grammatical viewpoint. So what is the last element in a list?

last(X, Xs) :-
   phrase( ( ..., [X] ), Xs).

... --> [] | [_], ... . % any sequence

Upvotes: 3

SQB
SQB

Reputation: 4078

Because the tail of a list is a list itself.

A list in Prolog can be seen as [H | T], where H is the first element (the head), and T (the tail) is a list of all other elements.

Your list [a, b, c] is actually [a | [b | [c | [] ] ] ] when you decompose it (the last tail is always an empty list):

List: [a, b, c]   Head: a   Tail: [b, c]
List: [b, c]      Head: b   Tail: [c]
List: [c]         Head: c   Tail: []

So when you get to the point where you're asking if last(c, [b, c]), that decomposes to last(c, [b|[c]]), which is why the first clause can't be instantiated, because X can't be both c and [c]. That is why it did work when you asked last([c],[a,b,c]).

The version proposed by alpha works, because it takes this into account:

% A list ends with an element if contains exactly that element.
last(X,[X]).

% A list ends with an element if its tail ends with that element.
% (Since we don't care about the head, we use an underscore).
last(X,[_|T]) :- last(X,T).

Upvotes: 2

traitor
traitor

Reputation: 318

You might want this:

 last(X,[X]).
 last(X,[_|Z]) :- last(X,Z).

The | denotes a 'tail' or 'the rest of the list.'

With ?- last(c,X). Prolog produces lists (according to your first definition) that have c as the last item.

When you query ?- last(c,[a,b,c])., it returns false because you haven't defined a case for a list of only one item [X] or [X|[]]. So it fails when list is shorter than two items.

However, last([c],[a,b,c]) succeeds because you get [b|_29] or whatever denoting that the tail part might be any list. So it '_29' can be '[c]', satisfying the first definition like last([c],[b|[c]]). Remember that a nonempty list in Prolog is actually a pair of the first list item (head) and a list of the rest (tail). Usually written as [head|tail].

Upvotes: 11

Related Questions