Prolog - return second to last list element - prolog

puzzling over a problem of trying to return the second to last element in a list written in Prolog. This language is interesting to use but I'm having trouble getting my head wrapped around it. Here is what I have:
secondLast([X], X).
secondLast(X, [Y], X) :- secondLast(Y, K).
secondLast(X, [Y|Z], K) :- secondLast(Y, Z, K).
secondLast([X|Z], Ans) :- secondLast(X, Z, Ans).
so calling secondLast([a, b, c, d], X).
X should equal c.
Any ideas?
Thanks!

you should apply pattern matching:
secondLast([X,_], X).
secondLast([_|T], X) :- secondLast(T, X).

Can be just:
secondLast(L, X) :-
append(_, [X, _], L).

Sergey and CapelliC have offered two nice solutions to the problem. Let's have a look to see what's wrong with the original:
1) secondLast([X], X).
2) secondLast(X, [Y], X) :- secondLast(Y, K).
3) secondLast(X, [Y|Z], K) :- secondLast(Y, Z, K).
4) secondLast([X|Z], Ans) :- secondLast(X, Z, Ans).
In Prolog, since it is about defining relations between entities with predicates, not defining functions, it helps to describe what a predicate means in terms of, "Something is true if some other things are true". The if in Prolog is expressed as :-.
We'll look at clause #4 since this appears to be your main clause. This one says, Ans is the second to last element of [X|Z] if Ans is the second to last element of Z with X as the head. It's unclear what this 3-argument version of secondLast means. However, if the list is 3 or more elements, it seems clear that X will become irrelevant (as will be seen in clauses 2 and 3).
Clause #1 says, X is the second to last element in list [X]. However, the element X is the last and only element in the list [X]. So this clause is logically incorrect.
Clauses #2 is a bit confusing. It introduces a variable in the clause, K, which is only used once and not defined or used anywhere else in the clause. It also ignores X because, as described above, it has become irrelevant since it's no longer a candidate for second-to-last element. Prolog has given you a warning about singleton elements K and X, which is similar to the warning in C that a variable is "defined but never used" or "is assigned a value that is never used". Clause #3 has the same issue.
In all that, I think I can see what you were trying to do, which is to say that, Ans is second to last element of [X|Z] if there's one more element after X, which would be true, but would be limited to being correct if the list [X|Z] is a 2-element list. In other words, your main clause almost assumes that the answer is ultimately X. If it isn't, it attempts to introduce a new candidate, Y in clauses 2 and 3, but this candidate has no way to "make it back" to the original, main clause.
I'll go back now to CapelliC's solution and describe how one comes to it:
1) secondLast([X,_], X).
2) secondLast([_|T], X) :- secondLast(T, X).
The first clause says, X is the second to last element in the 2-element list, [X,_] which is true. And we don't care what the last element is so we just call it, _. We could have called it Y ([X,Y]), but then Prolog would warn about a singleton variable since we don't need or use Y.
The second clause says, X is the second to last element of list [_|T] if X is the second to last element of the tail, T. This is also true of any list that is 3 or more elements. That's fine since the base case, clause one, takes care of the 2-element list. Clause two will, recursively, reduce down to clause one and finally succeed with the right answer. In this second clause, if X is taken from T, then we don't care what the head of the list is since it has become irrelevant, so we use _ as the head in this case (this corresponds to the X in your original clause #4).
In Sergey's answer:
secondLast(L, X) :-
append(_, [X, _], L).
This says, X is second to last element in list L if L is a two element list with X as the first element ([X,_]) appended to the end of some other list (_). Note again that we're using _ for the variables which will have values but we don't care what those values are in this case. So, for example: 2 is the second to last element of [1,2,3] if [1,2,3] is [2,_] appended to some other list and it is: if you append [2,3] to [1] you get [1,2,3].

Related

Repeat and Double elements in lists in Prolog

how can I write two predicates that are described below.
1) Define the double(X,Y) predicate, which is true if the list Y contains each of the elements X
repeated twice. Example: double([a,b],[a,a,b,b]) is true.
2) Define the predicate repeat(X,Y,N), which is true if the list Y contains each of the elements X
repeated N times. For example, for the question repeat([a,b],[a,a,a,b,b,b],3), Prolog answers true.
Could you give me the example of those predicates?
If you have repeat/3 you have double/2.
and thus:
multiple(X,N,R) :-
length(R,N),maplist(=(X),R).
repeat(Li,Lo,N) :-
maplist({N}/[Xi,Xo]>>multiple(Xi,N,Xo),Li,Nested),flatten(Nested,Lo).
But it doesn't run backwards due to the flatten/2 I think. Can that be improved?
double([], []).
double([X|Y], [X,X|Z]) :- double(Y,Z).
remove_if_same(_,R,0,R):- !.
remove_if_same(X,[X|Y],N,R) :- Nm1 is N-1,remove_if_same(X,Y,Nm1,R).
repeat([],[],_).
repeat([X|Xr],Y,N) :- remove_if_same(X,Y,N,R), repeat(Xr,R,N).
How double works?
If you've got two empty lists, then that is true, there is nothing to double from the first argument.
Otherwise, you're taking the head from the first list, and 2 head elements from the second list. If all these are the same (so if all are X) you're checking with recursion rest of elements, so accordingly Y and Z. So you'll check once again if lists are empty and so on, and if on any of the steps sth is not possible you return false.
About the repeat predicate, it's quite similar in reasoning.
2 things that I should explain:
The ! mark will make that the command-line interface(like swipl) will not search for other results of the remove_if_same. It would work same if we pass it to the repeat.
remove_if_same statement uses the accumulator (the 4th argument) to return at the end of the search the list without N same elements.

Understanding difference lists (Prolog)

I'm having trouble understanding difference list, particularly in this predicate:
palindrome(A, A).
palindrome([_|A], A).
palindrome([C|A], D) :-
palindrome(A, B),
B=[C|D].
Could anyone help me follow what's happening?
palindrome(A, A).
palindrome([_|A], A).
palindrome([C|A], D) :-
palindrome(A, B),
B=[C|D].
Seeing the arguments to this predicate as a difference list, the first clause says, a list from A to A (i.e., an empty list) is a palindrome.
The second clause says, a one-element list is a palindrome, whatever that one element is.
Don't panic! Difference lists are just lists with explicit end "pointer"
A normal list, say [1,2,3], is a difference between its start and its end; the end of a normal list is always an empty list, []. That is to say, for a list [1,2,3] we are supposed to call this predicate as palindrome( [1,2,3], []) — namely, check whether the difference list [1,2,3] - [] is a palindrome.
From the operational point of view, a difference list is nothing but a (possibly open-ended) list with explicitly maintained "end pointer", for example: A - Z where A = [1,2,3|Z] and Z = []. Indeed, [1,2,3|[]] is the same as [1,2,3]. But when Z is not instantiated yet, the list A is still open ended - its "end pointer" Z can be instantiated to anything (but only once, of course, sans the backtracking).
If we were to instantiate Z later to an open-ended list, say, Z = [4|W], we'd get a new, extended difference list A - W where A = [1,2,3,4|W]. The old one would become A - Z = [1,2,3,4|W] - [4|W], i.e. still representing a prefix [1,2,3] of an open-ended list [1,2,3,4 ...]. Once closed, e.g. with W = [5], all the pairs of logvars still represent their corresponding difference lists (i.e. A - Z, A - W ...), but A is not open-ended anymore, so can't be extended anymore.
Instead of using the - functor, it is customary to just use both parts of the diff list definition as separate arguments to a predicate. When we always use / treat them as if they were two parts of a pair, then they form a pair, conceptually. It's the same thing.
Continuing. The third clause says, for [C|A]-D to be a palindrome, A-B must be a palindrome, and B must be [C|D]. A, D, B are lists, C is an element of a list. This might be confusing; let's use V instead. Also, use Z and Y instead of D and B, to remind us of "the end" of a list:
palindrome([V|A], Z):- palindrome(A, Y), Y=[V|Z].
V ................. V ----
^ ^ ^
| | |
| | Z
A Y = [V|Z]
Indeed, when the ...... core is a palindrome, putting two Vs around it gives us another palindrome.
The following is a summary that hopefully distills the best of the previous discussion, and adds one small but significant simplification.
First, the original question should be understood in the context of the problem at hand, which can be formulated as defining a Prolog predicate which will check whether a list is a palindrome, or more generally to generate palindromes. We wish to explore an implementation using difference lists, so we can begin as follows:
% List is a palindrome if List - [] is a palindrome:
palindrome( List ) :- palindrome(List, []).
(As explained elsewhere, if a list, List, is the concatenation of two lists
Front and Back, then Front can be viewed as being the difference
between List and Back, that is, Front can be regarded as equivalent to (List - Back).)
To define palindrome/2, we begin with the two "base cases", an empty list and a singleton:
% The empty list (L-L) is a palindrome:
palindrome(L, L).
% A singleton list, ([X|L] - L), is a palindrome:
palindrome([X|L], L).
Let us now turn to the general case.
If a list with more than one element is to be a palindrome, then it
will look like this: E ... E
where ... is a (possibly empty) palindrome.
Tacking on a tail, Tail, our list must look like: E ... E Tail
Writing this regular list as [E|Rest], we can now see that the original list ( [E|Rest] - Tail ) is a palindrome if (Rest - [E|Tail]) is a palindrome,
or in terms of our predicate palindrome/2:
palindrome( [E|Xs], Tail ) :- palindrome(Xs, [E|Tail]).
It's easy to see that this is equivalent to the original formulation.
That's it! Now we can, for example, generate templates for palindromes:
?- palindrome( X ).
X = [] ;
X = [_G1247] ;
X = [_G1247, _G1247] ;
X = [_G1247, _G1253, _G1247] ;
X = [_G1247, _G1253, _G1253, _G1247]
....

Prolog - how to understand lists

I don't know much, how to understand that fact p([H|T], H, T). I know C/C++/Java etc.. but this looks diferrent. So when i pass first argument to "function" p, it separates it into H and T and makes it accessible through this vars? I don't know how to logically understand this.
http://www.doc.gold.ac.uk/~mas02gw/prolog_tutorial/prologpages/lists.html
p([H|T], H, T).
Lets see what happens when we ask some simple queries.
?- p([a,b,c], X, Y).
X=a
Y=[b,c]
yes
In Prolog we have relations, in a way similar to relationals DBs.
Then p/3 it's a relation among a list (first argument), its head H and its tail T.
Appropriately the tutorial' author used descriptive and synthetic symbols as Variables.
Syntactically, variables are symbols starting Uppercase and can get any value, but only one time (that is, cannot be 'reassigned').
The page you refer to says, "Consider the following fact.
p([H|T], H, T)."
So we must treat this as a fact. That means, it's like having a predicate
p([H|T], H, T):- true. % or, p(L,H,T) :- L=[H|T].
Now, when you query p([a,b,c], X, Y)., one is put besides the other:
p([a,b,c], X, Y). % a query
p([H|T], H, T) :- true. % a rule's clause: _head_ :- _body_.
the equivalences are noted: [a,b,c] = [H|T], X = H, Y = T and treated as unification equations. The first gets further translated into
a = H % list's head element
[b,c] = T % list's tail
because [A|B] stands for a list with A the head element of the list, and B the list's tail, i.e. all the rest of its elements, besides the head. H and T are common mnemonic names for these logical variables.
So on the whole, we get X = H = a, Y = T = [b,c].
This process is what's known as unification of a query and a rule's head (the two things starting with a p "functor", and both having the 3 "arguments").
Since the query and the head of a rule's "clause" matched (had same functor and same number of arguments), and their arguments were all successfully unified, pairwise, using the above substitution, we only need to prove the body of that rule's clause (that was thus selected).
Since it is true, we immediately succeed, with the successful substitution as our result.
That's how Prolog works.
TL;DR: yes, when you call p(L,H,T) with a given list L, it will be destructured into its head H and tail T. But you may call it with a given list T, a value H, and a variable L, and then a new list will be constructed from the head and the tail. And if L is given as well, it will be checked whether its head is H, and its tail is T.
This is because Prolog's unification is bi-directional: A = B is the same as B = A. Unification with a variable is like setting that variable, and unification with a value is like checking the (structural) equality with that value.
Calling p(L,H,T) is really equivalent to the unification L = [H|T].

How to say if an element belongs to a list in Prolog?

I have this simple program that checks if an X element belongs to a list:
member2(X, [X|_]).
member2(X,[_|T]):- member2(X,T).
I'm trying to write it in an extended form (because in the previous way the behavior is not so clear). So I have write it in the following way:
member2(X, [X|_]).
member2(X,Y):- Y = [_|T],
member2(X,T).
So the meaning is more clear:
I have one fact that represent the base case (the X element belongs to the list if it is in the head of the list).
The rule say that I have to prove two things:
1) Y = [_|T]. This is true because the anonymous variable _ unifies with anything.
2) It recursively search the element X in the tail list.
Ok, I think my reasoning is correct but this second version of the program don't work! I think that maybe the problem could be in the Y = [_|T] section
your program is fine, in both forms. Here yet another way to (re)write it
member2(X, [Y|Ys]) :-
X = Y ; member2(X, Ys).
I guess the textbook example of checking for a member is:
member_check(X, [X|Tail]). % or [X|_] to avoid the warning
member_check(X, [Y|Tail]) :- X \= Y, member_check(X, Tail).
which would probably be clearer than your second attempt?

Passing results in prolog

I'm trying to make a function that has a list of lists, it multiplies the sum of the inner list with the outer list.
So far i can sum a list, i've made a function sumlist([1..n],X) that will return X = (result). But i cannot get another function to usefully work with that function, i've tried both is and = to no avail.
Is this what you mean?
prodsumlist([], 1).
prodsumlist([Head | Tail], Result) :-
sumlist(Head, Sum_Of_Head),
prodsumlist(Tail, ProdSum_Of_Tail),
Result is Sum_Of_Head * ProdSum_Of_Tail.
where sumlist/2 is a SWI-Prolog built-in.
Usage example:
?- prodsumlist([[1, 2], [3], [-4]], Result).
Result = -36.
The part "it multiplies the sum of the inner list with the outer list" isn't really clear, but I believe you mean that, given a list [L1,...,Ln] of lists of numbers, you want to calculate S1*..*Sn where Si is the sum of the elements in Li (for each i).
I assume the existence of plus and mult with their obvious meaning (e.g. plus(N,M,R) holds precisely when R is equal to N+M). First we need predicate sum such that sum(L,S) holds if, and only if, S is the sum of the elements of L. If L is empty, S obviously must be 0:
sum([],0).
If L is not empty but of the form [N|L2], then we have that S must be N plus the sum S2 of the elements in L2. In other words, we must have both sum(L2,S2) (to get S2 to be the sum of the elements of L2) and plus(N,S2,S). That is:
sum([N|L2],S) :- sum(L2,S2), plus(N,S2,S).
In the same way you can figure out the predicate p you are looking for. We want that p(L,R) holds if, and only if, R is the product of S1 through Sn where L=[L1,...,Ln] and sum(Li,Si) for all i. If L is empty, R must be 1:
p([],1).
If L is not empty but of the form [LL|L2], then we have that R must be the product of 'S', the sum of the elements of LL, and 'P', the product of the sums of the lists in L2. For S we have already have sum(LL,S), so this gives us the following.
p([LL|L2],R) :- sum(LL,S), p(L2,P), mult(S,P,R).
One thing I would like to add is that it is probably not such a good idea to see these predicates as functions you might be used to from imperative or functional programming. It is not the case that sumlist([1,..,n],X) returns X = (result); (result) is a value for X such that sumlist([1,...,n],X) is true. This requires a somewhat different mindset. Instead of thinking "How can I calculate X such that p(X) holds?" you must think "When does P(X) hold?" and use the answer ("Well, if q(X) or r(X)!") to make the clauses (p(X) :- q(X) and p(X) :- r(X)).
Here is a rewrite of Kaarel's answer (that's the intention anyway!) but tail-recursive.
prodsumlist(List, Result) :-
xprodsumlist(List,1,Result).
xprodsumlist([],R,R).
xprodsumlist([Head|Rest],Sofar,Result) :-
sumlist(Head, Sum_Of_Head),
NewSofar is Sofar * Sum_Of_Head,
xprodsumlist(Rest, NewSofar, Result).

Resources