Prolog - Find second and second last elements in list - prolog

I am new to prolog and currently stuck trying to understand how to implement this.
I need a predicate to find the second and the second last elements of a list using recursion, so for example:
second_secondLast([1,2], X, Y). must return X=2, Y=1.
second_secondLast([1,2,3], X, Y). must return X=2, Y=2.
second_secondLast([1], X, Y). must print 'Error' and return false.
First, I have the error-checking clauses:
second_secondLast([], X, Y) :- print("Error"), !, fail.
second_secondLast([_], X, Y) :- print("Error"), !, fail.
Next, I tried something like this:
second_secondLast([Y,X],X,Y) :- !.
second_secondLast(L, X, Y) :-
second(L,X),
secondLast(L,Y).
second([_,S|_], X) :- X = S.
secondLast([P,_], Y) :- Y = P.
secondLast([F|R], Y) :- secondLast(R, Y).
However, the output using [1,2,3] is X=Y, Y=2.
I'm not sure if it is possible to force the output to be X=2 instead, or if there is a better method to do this.

First of all, the output X=Y, Y=2. has nothing to do with your program, it is an idiosyncracy of swipl (and maybe other interactive environments for Prolog implementations).
I think, your program looks fine, but you are asking for possible improvements.
second([_,S|_], S). is a more elegant version of your second([_,S|_], X) :- X = S..
Likewise, secondLast([P,_], P). is more elegant than your secondLast([P,_], Y) :- Y = P..
I would also prefer secondLast([_|R], Y) :- secondLast(R, Y). to your
secondLast([F|R], Y) :- secondLast(R, Y)..
Your error-checking clauses look fine to me.
You could also get rid of the predicate second and alter the definition of second_secondLast by using
second_secondLast([H,X|T], X, Y):-
secondLast([H,X|T], Y).
instead of your
second_secondLast(L, X, Y) :-
second(L,X),
secondLast(L,Y).
That change would also make it a bit more efficient.
Another possibility is to use
second_secondLast(L, X, Y):-
L= [_,X|_],
secondLast(L, Y).
Then you could also get rid of the predicate secondLast and alter the above clause to
second_secondLast(L, X, Y):-
L= [_,X|_],
append(_, [Y,_], L).
.
There is always a ton of possibilities...

Related

Why do i get a stack limit exceeded error when defining a predicate that convert the relation of two atoms?

I want to know why does the program goes in an infinite recursion in those cases:
?- love(kay, amanda).
and
?- love(rob, amanda).
And here is the code:
love(amanda, kay).
love(kay, geo).
love(geo, rob).
love(X, Y) :-
love(X, Z),
love(Z, Y).
love(X, Y) :-
love(Y, X).
First, your program always goes into an infinite loop. No matter what names you are using. Even ?- loves(amanda, kay). loops. To better see this rather ask ?- loves(amanda, kay), false. Why am I so sure that your program always loops?
To see this, we need some falsework. By adding goals false into your program, we get a new program requiring less (or equal) inferences.
love(amanda, kay) :- false.
love(kay, geo) :- false.
love(geo, rob) :- false.
love(X, Y) :-
love(X, Z), false,
love(Z, Y).
love(X, Y) :- false,
love(Y, X).
Because this fragment (called a failure-slice) does not terminate, your original program will not terminate. As you can see, all the persons have been removed. And thus actual names cannot influence the outcome.
If you want to fix commutativity, rather introduce a further predicate:
love2(X, Y) :- love1(X, Y).
love2(X, Y) :- love1(Y, X).
And to include the transitive closure, use closure/3:
love3(X, Y) :-
closure(love2, X, Y).

Function not in prolog

sibling(X, Y):- father(Z, X), father(Z, Y), not (X=Y).
sister(X, Y):- father(Z, X), father(Z, Y), female(X).
brother(X, Y):- father(Z, X), father(Z, Y), male(X).
i'm having a bit problem with using the not function. i've tried not X=Y. but to no avail, the sibling rule still produce error.
if i were to delete the not x=y, the output will be a bit kind of "ugly".
how should i write the not function?
The ISO predicate implementing not provable is called (\+)/1.
However, as #coder explains in the comments, it is much better to use dif/2 to express that two terms are different.
dif/2 is a pure predicate that works correctly in all directions, also if its arguments are not yet instantiated.
For example, with (\+)/1, we get:
?- \+ (X = Y ).
false.
No X and Y exist that satisfy this goal, right? Wrong:
?- X = a, Y = b, \+ (X = Y ).
X = a,
Y = b.
In contrast, with dif/2:
?- dif(X, Y).
dif(X, Y).
and in particular:
?- X = a, Y = b, dif(X, Y).
X = a,
Y = b.
See prolog-dif for more information. dif/2 is with us since the very first Prolog system. I strongly recommend you use it.
SWI Prolog has no notoperator. it can be used as a regular compound term, e.i. not(X).
It must be no space between functor and open parenthesis:
foo( argument list ).
This is the cause of the error.
SWI Prolog suggests ISO-standard replacement for not/1: (\+)/1

Get unique results with Prolog

I have this Prolog code that returns: [[vincent,vincent],[vincent,marcellus],[marcellus,vincent],[marcellus,marcellus],[pumpkin,pumpkin],[honey_bunny,honey_bunny]].
:- initialization main.
loves(vincent, mia).
loves(marcellus, mia).
loves(pumpkin, honey_bunny).
loves(honey_bunny, pumpkin).
jealous(X, Y) :-
loves(X, Z),
loves(Y, Z).
main :-
findall([X, Y], jealous(X, Y), L),
write(L),
halt.
How to get the only results when X != Y?
I tried the following code to get the same results as before.
jealous(X, Y) :-
X \== Y,
loves(X, Z),
loves(Y, Z).
With \=, I got [].
How to get only [vincent,marcellus] as a result?
The order of the goals in your attempted solution is wrong. When called with two distinct variables, the (\==)/2 standard predicate always succeed. The solution is to call the predicate only when its arguments are instantiated:
jealous(X, Y) :-
loves(X, Z),
loves(Y, Z),
X \== Y.
With this fix, your query now returns:
?- findall([X, Y], jealous(X, Y), L).
L = [[vincent, marcellus], [marcellus, vincent]].
So, no one is jealous of himself anymore. But you still get a redundant solution. We can modify the jealous/2 predicate to sort the names in the returned solutions. For example:
jealous(X, Y) :-
loves(X0, Z),
loves(Y0, Z),
X0 \== Y0,
( X0 #< Y0 ->
X = X0, Y = Y0
; X = Y0, Y = X0
).
Now, by using setof/3 instead of findall/3, we get:
?- setof([X, Y], jealous(X, Y), L).
L = [[marcellus, vincent]].
One final observation. A list is a poor solution for representing a pair. The traditional way is to use either X-Y or (X, Y).
Whenever possible, use dif/2 instead of (\==)/2.
dif/2 will help you write logically sound programs.
For details, look at prolog-dif!

Prolog: script doesn't halt

I'm currently trying to learn prolog. I hope you can help..
I have three rules:
reverse - retrieves the reverse of a list
startswith - checks if the second list is a prefix of the first list
suffix - checks if the first list is a suffix of the second list
reverse([H|T], Y) :- append(Z, [H], Y), reverse(T, Z).
reverse([], Y) :- Y = [].
startswith(_, []).
startswith([Xh|Xt], [Yh|Yt]) :- Xh=Yh, startswith(Xt, Yt).
suffix(X, Y) :- reverse(X, XR), reverse(Y, YR), startswith(YR,XR).
reverse and startswith seem to work as they should.
But suffix doesn't stop calculating. I cannot understand why?
In general, it's a bad idea to pass variables that have yet to be unified to predicates that you will unify later. Essentially, append(Z,[H],Y) is spiraling off into prolog never-never land because it's unifying Z over and over based on your version of Prolog's append.
Change
reverse([H|T], Y) :- append(Z, [H], Y), reverse(T, Z).
To
reverse([H|T], Y) :- reverse(T, Z), append(Z, [H], Y).
So that you unify Z before you pass it to append

Simple prolog program returns false too early

It's been a while since I've programmed in Prolog. Today, I tried to make a simple program. It lists some facts of who belongs to the same family. If two people belong to the same family, they cannot give eachother gifts. I want to get all the people (or at least one person) to whom someone is allowed to give a gift.
family(john, jack).
family(matt, ann).
family(ann, jack).
family(jordan, michael).
family(michael, liz).
sameFamily(X, Y) :-
family(X, Y).
sameFamily(X, X) :-
false.
sameFamilySym(X, Y) :-
sameFamily(X, Y).
sameFamilySym(X, Y) :-
sameFamily(Y, X).
sameFamilyTrans(X, Z) :-
sameFamilySym(X, Y),
sameFamilySym(Y, Z).
gift(X, Y) :-
not(sameFamilyTrans(X, Y)).
Some queries if sameFamilyTrans/2 return false when they should in fact return true.
sameFamilyTrans/2 is obviously wrong. I think I need to keep a list of intermediate transitivities. Something like this:
sameFamilyTrans(X, Z, [Y|Ys]) :-
sameFamilySym(X, Y, []),
sameFamilyTrans(Y, Z, Ys).
But then I don't know how to call this.
P.S. I am using SWI-Prolog, if that makes any difference.
Yes, you were on the right track. The trick is to call the transitive closure with an empty accumulator, and check in each step whether a cycle is found (i.e., whether we have seen this member of the family before. As "false" has pointed out, the persons need to be instantiated already before going into the not, though.
So in sum, this works:
family(john, jack).
family(matt, ann).
family(ann, jack).
family(jordan, michael).
family(michael, liz).
sameFamily(X, Y) :-
family(X, Y).
sameFamilySym(X, Y) :-
sameFamily(X, Y).
sameFamilySym(X, Y) :-
sameFamily(Y, X).
sameFamilyTrans(X, Y, Acc) :-
sameFamilySym(X, Y),
not(member(Y,Acc)).
sameFamilyTrans(X, Z, Acc) :-
sameFamilySym(X, Y),
not(member(Y,Acc)),
sameFamilyTrans(Y, Z, [X|Acc]).
person(X) :- family(X, _).
person(X) :- family(_, X).
gift(X, Y) :-
person(X),
person(Y),
X \= Y,
not(sameFamilyTrans(X, Y, [])).
A bit of background: Transitive closure is not actually first-order definable (cf. https://en.wikipedia.org/wiki/Transitive_closure#In_logic_and_computational_complexity). So it can be expected that this would be a little tricky.
Negation is implemented in Prolog in a very rudimentary manner. You can essentially get a useful answer only if a negated query is sufficiently instantiated. To do this, define a relation person/1 that describes all persons you are considering. Then you can write:
gift(X,Y) :-
person(X),
person(Y),
\+ sameFamily(X,Y).
There is another issue with the definition of sameFamily/2.

Resources