Prolog predicate issue - prolog

I'm having an issue with a part of my assignment. I'm supposed to write a predicate "friendly" which is supposed to be true when network member X is said to be friendly if X likes back everyone who likes him/her.
EDIT: In the below example, barry is friendly as the list of people that like barry is kara and barry likes kara back. Kara is NOT the right answer as the list of people that like Kara are Barry, Clark and Oliver BUT Kara only likes back Barry and Clark so Kara is not friendly.
eg of G = [person(kara, [barry, clark]),person(bruce, [clark, oliver]),person(barry, [kara, oliver]),person(clark, [oliver, kara]),person(oliver, [kara])]
What I have so far;
friendly(G, X):-
member_(person(X, _), G),
likers(G, X, L),
likes_all(G, X, L).
% to get the list of members who like X;
likers(G, X, [Y|T]) :-
likes(G, Y, X),
select_(Y, G, G2),
likers(G2, X, T).
likers([], _, []).
likers([], _, _).
likers(_, _, []).
% select is used to remove the person from the list once visited.
select_(X, [person(X, _)|T], T).
select_(X, [H|T], [H|R]) :-
select_(X, T, R).
% to check whether X likes all the list of people that like X;
likes_all(G, X, [H|T]):-
likes(G, X, H),
likes_all(G, X, T).
likes_all(_, _, []).
likes_all(G, [H|T], X):-
likes(G, H, X),
likes_all(G, T, X).
likes_all(_, [],_).
likes(G, X, Y):-
member_(person(X, L), G),
member_(Y, L).
member_(X, [X|_]).
member_(X, [_|T]) :-
member_(X, T).
My issue is it doesn't work properly. See sample output below.
So, I don't know what is wrong and how I'm supposed to do it. We are not allowed to use any built in predicates or control operators, so no !, ;, =, \=, +, etc, only pure prolog.
Any hint on moving forward is appreciated.
Output:
[debug] ?- friendly([person(kara, [barry, clark]),person(bruce, [clark, oliver]),person(barry, [kara, oliver]),person(clark, [oliver, kara]),person(oliver, [kara])], X).
X = kara ;
X = kara ;
X = kara ;
X = kara ;
X = kara ;
X = bruce ;
X = barry ;
X = barry ;
X = clark ;
X = clark ;
X = oliver ;
false.
I think my error is somewhere in likers function. Output of "likers":
?- likers([person(kara, [barry, clark]), person(bruce, [clark, oliver]), person(barry, [kara, oliver]), person(clark, [oliver, kara]), person(oliver, [kara])], kara, L).
L = [] ;
L = [barry] ;
L = [barry, clark] ;
L = [barry, clark, oliver] ;
L = [barry, oliver] ;
L = [barry, oliver, clark] ;
L = [clark] ;
L = [clark, barry] ;
L = [clark, barry, oliver] ;
L = [clark, oliver] ;
L = [clark, oliver, barry] ;
L = [oliver] ;
L = [oliver, barry] ;
L = [oliver, barry, clark] ;
L = [oliver, clark] ;
L = [oliver, clark, barry] ;
false.
In the above, the right answer would be L = [barry, clark, oliver] or one of the combinations. Is there a way to get that in pure prolog ?

After calming down, and a bit of google, here is an answer that ought to be satisfactory. The key idea is to pass around not only the person X we are interested in but also the complement set (AEX). Comparing against this is equivalent to negation of comparison with X.
friendly(X) :-
G = [person(kara,[barry,clark]),
person(bruce,[clark,oliver]),
person(barry,[kara,oliver]),
person(clark,[oliver,kara]),
person(oliver,[kara])],
allppl(G,All),
mymember(person(X,Xs),G),
select(X,All,AEX),
likers(G,X,AEX,[],Fs),
subset(Fs,Xs).
Get a list of all people:
allppl([person(P,_)|Rest],[P|Ps]) :-
allppl(Rest,Ps).
allppl([],[]).
This my old likers/4 plus the complement set.
likers([person(Y,Ys)|Rest],X,AEX,Fs0,Fs) :-
mymember(X,Ys),
likers(Rest,X,AEX,[Y|Fs0],Fs).
likers([person(_,Ys)|Rest],X,AEX,Fs0,Fs) :-
subset(Ys,AEX),
likers(Rest,X,AEX,Fs0,Fs).
likers([],_,_,Fs,Fs).
And here are some helper predicates.
select(X,[X|Xs],Xs).
select(X,[Y|Xs],[Y|Zs]) :-
select(X,Xs,Zs).
subset([],_).
subset([X|Xs],Ys) :-
mymember(X,Ys), subset(Xs,Ys).
mymember(X,[X|_]).
mymember(X,[_|Xs]) :-
mymember(X,Xs).
Now I get
?- friendly(X).
X = bruce ? ;
X = barry ? ;
no

Here's how your program should probably be written:
person(kara,[barry, clark]).
person(bruce,[clark,oliver]).
person(barry,[kara,oliver]).
person(clark,[oliver,kara]).
person(oliver,[kara]).
likes_back([],_).
likes_back([Y|Ys],X) :-
person(Y,Xs),
member(X,Xs),
likes_back(Ys,X).
friendly(X) :-
person(X,Ys),
likes_back(Ys,X).
?- friendly(X),write(X),nl,fail.
When I run it I only get kara as a result - which is correct based on my inspection of the data and rules you gave.

Ok, here is a solution then, if I understand correctly. I assume that people that nobody likes (Bruce) are not considered friendly.
friendly(P) :-
person_likes(P,Ps),
findall(X,(person_likes(X,Zs),member(P,Zs)),Fs), Fs \= [],
check_subset(Fs,Ps).
check_subset([X|Xs],Set) :-
memberchk(X,Set),
check_subset(Xs,Set).
check_subset([],_).
person_likes(kara,[barry,clark]).
person_likes(bruce,[clark,oliver]).
person_likes(barry,[kara,oliver]).
person_likes(clark,[oliver,kara]).
person_likes(oliver,[kara]).
Result:
?- friendly(X).
X = barry ? ;
no
If you are not allowed to use findall/3 then you need to write another three lines of code or so yourself.

You could write likers/4 like this:
likers([person(Y,Ys)|Rest],X,Fs0,Fs) :-
( memberchk(X,Ys) ->
Fs1 = [Y|Fs0]
; Fs1 = Fs0 ),
likers(Rest,X,Fs1,Fs).
likers([],_,Fs,Fs).
Call it with third parameter empty list.
Addition: without semicolon:
likers([person(Y,Ys)|Rest],X,Fs0,Fs) :-
memberchk(X,Ys),
likers(Rest,X,[Y|Fs0],Fs).
likers([person(_,Ys)|Rest],X,Fs0,Fs) :-
\+ memberchk(X,Ys),
likers(Rest,X,Fs0,Fs).
likers([],_,Fs,Fs).

Related

Prolog taller than

What is the easiest way to find who is the tallest in Prolog:
height(lisa,1.65).
height(sam,1.70).
height(luke,1.92).
height(nicole,1.54).
I want to write
tallest(X) :- Y is bigger than other Y's
SWI-Prolog has some different ways to solve this problem, for instance by means of library(solution_sequences)
?- order_by([desc(H)],height(P,H)).
H = 1.92,
P = luke ;
...
or using library(aggregate):
?- aggregate(max(H,P),height(P,H),max(_,P)).
P = luke.
less sophisticate Prologs probably will offer setof/3 and last/2:
?- setof(H:P,height(P,H),L),last(L,_:P).
P = luke,
L = [1.54:nicole, 1.65:lisa, 1.7:sam, 1.92:luke].
and still more basic engines, lacking setof/3, will offer
?- height(P,H),\+((height(_,L),L>H)).
P = luke,
H = 1.92 ;
Supposing that tallest(X) succeeds if, and only if, person X is taller than all other persons, I think that a correct answer would be:
tallest(X) :-
height(X, H),
forall((height(Y, H1),
X \= Y),
H > H1), !.
First scenario:
height(lisa,1.65).
height(sam,1.70).
height(luke,1.92).
height(nicole,1.54).
?- tallest(X).
X = luke.
Second scenario:
height(lisa, 1.65).
height(sam, 1.70).
height(luke, 1.92).
height(nicole, 1.54).
height(bob, 1.92). % Bob is as tall as Luke!
?- tallest(X).
false.
height(lisa,1.65).
height(sam,1.70).
height(luke,1.92).
height(nicole,1.54).
max_height(Person, Height, [[Person, Height]]).
max_height(P , H , [[P, H]|Tail]) :- max_height(_ , H2, Tail), H > H2.
max_height(P2, H2, [[_, H]|Tail]) :- max_height(P2, H2, Tail), H =< H2.
tallest(X) :- findall([P, H], height(P, H), Bag), max_height(X, _, Bag).
There are ways to avoid writing max_height : Prolog, find minimum in a list

Nephew of my own father in prolog

i'm programming a family in prolog and i'm having trouble with the nephew implementation. When I ask if erick is the nephew of Alberto it returns true, when it should return false because Alberto is the father of erick, however, it does works for all the other cases that should be true. If someone could help me I would be very grateful.
My code:
man(beto).
man(fransisco).
man(alberto).
man(jaime).
man(manolo).
man(nolo).
man(lito).
man(manuel).
man(erick).
man(jesu).
man(jesus).
woman(emi).
woman(harumi).
woman(haru).
woman(yuneisi).
woman(yasmeli).
woman(mioara).
woman(elia).
woman(iza).
woman(alice).
woman(ofelia).
woman(arlet).
parent(manuel, alberto).
parent(ofelia, alberto).
parent(manuel, jaime).
parent(ofelia, jaime).
parent(manuel, manolo).
parent(ofelia, manolo).
parent(alberto, erick).
parent(alberto, beto).
parent(alberto, fransisco).
parent(emi, erick).
parent(emi, beto).
parent(manolo, nolo).
parent(manolo, arlet).
parent(nolo, lito).
parent(iza, lito).
parent(mioara, yuneisi).
parent(mioara, yasmeli).
parent(jaime, yuneisi).
parent(jaime, yasmeli).
parent(jesus_padre, jesu)
parent(jesus_padre, alice).
parent(jesus_padre, haru).
parent(harumi, haru).
parent(harumi, jesu).
parent(harumi, alice).
father(X,Y) :- parent(X,Y), man(X).
mother(X,Y) :- parent(X,Y), woman(X).
brother(X,Y) :- man(X), parent(F, X), parent(F, Y).
sister(X,Y) :- woman(X), parent(P, X), parent(P, Y).
grandpa(X,Y) :- father(F,Y), father(X,F), man(X).
grandma(X,Y) :- father(F,Y), mother(X,F), woman(X).
son(X,Y) :- father(Y,X), man(X).
nephew(X,Y) :- father(F,X), brother(F,Y).
Besides the missing dot after parent(jesus_padre, jesu) as pointed out by #LuaiGhunim there are a few other issues with your predicates. Your definition of brother/2 is too general. Nobody is his own brother but if you query your predicate you find several such instances:
?- brother(X,X).
X = beto ;
X = beto ;
X = fransisco ;
X = alberto ;
X = alberto ;
X = jaime ;
X = jaime ;
X = manolo ;
X = manolo ;
X = nolo ;
X = lito ;
X = lito ;
X = erick ;
X = erick ;
X = jesu ;
X = jesu ;
false.
You can easily remedy this by adding a goal dif/2:
brother(X,Y) :-
dif(X,Y),
man(X),
parent(F, X),
parent(F, Y).
Now the query above fails as it should:
?- brother(X,X).
false.
You'll still get a lot of pairs twice:
?- brother(X,Y).
X = beto, % <- 1st occurrence
Y = erick ; % <- 1st occurrence
X = beto,
Y = fransisco ;
X = beto, % <- 2nd occurrence
Y = erick ; % <- 2nd occurrence
.
.
.
The reason for that is that you can derive it via the mother or the father. In the above example (beto and erick) you'll get there via emi or alberto. These solutions might be redundant but they are correct. The same goes for your predicate sister/2:
?- sister(X,X).
X = haru ;
X = haru ;
X = yuneisi ;
X = yuneisi ;
X = yasmeli ;
X = yasmeli ;
X = alice ;
X = alice ;
X = arlet.
The remedy is the same as above:
sister(X,Y) :-
dif(X,Y),
woman(X),
parent(P, X),
parent(P, Y).
?- sister(X,X).
false.
?- sister(X,Y).
X = haru,
Y = jesu ;
X = haru,
Y = alice ;
X = haru,
Y = jesu ;
.
.
.
Your definition of grandma/2 and grandpa/2 on the other hand is too specific. To see this let's add the following facts to your code:
man(m1).
man(m2).
woman(w1).
woman(w2).
woman(w3).
parent(m1,w1).
parent(w1,w2).
parent(w2,w3).
Then the following queries should succeed but they fail instead:
?- grandpa(m1,w2).
false.
?- grandma(w1,w3).
false.
The reason for this is that the intermediate parent in your definition of grandpa/2 and grandma/2 is a father/2 where it should be a parent/2. Additionally the last goals (man(X) and woman(X)) are redundant since they are already covered by father/2 and mother/2 respectively. Instead, you could define the two predicates like so:
grandpa(X,Y) :-
parent(F,Y),
father(X,F).
grandma(X,Y) :-
parent(F,Y),
mother(X,F).
Now the above queries yield the desired result:
?- grandpa(m1,w2).
true.
?- grandma(w1,w3).
true.
Finally, a nephew according to the Cambridge Dictionary is a son of your sister or brother, or a son of the sister or brother of your husband or wife. Since you haven't got predicates for husbands and wives I'll stick with the a son of your sister or brother part. If you add facts for husbands and wives, you can add additional rules to cover the other part of the definition. You can write the first part of the definition in Prolog like so:
nephew(X,Y) :-
man(X),
dif(F,Y),
parent(P,F),
parent(P,Y),
parent(F,X).
If you query this predicate there's no erick/alberto solution any more:
?- nephew(erick,X).
X = jaime ;
X = manolo ;
X = jaime ;
X = manolo ;
false.
Prolog it's all about relations. Joins plays a fundamental role. So, often you can think in term of 'yields', or functions over the DB, and design/control the data access plan by means of joins (clauses, i.e. logical formulae) producing records - like SQL does. The procedural execution over the retrieved data is expressed in (almost) the same language: joins, giving us a taste of declarative programming. Anyway, here are the clauses provided by #tas in standard Prolog:
brother(X,Y) :-
man(X),
parent(F, X),
parent(F, Y),
X\=Y.
sister(X,Y) :-
woman(X),
parent(P, X),
parent(P, Y),
X\=Y.

Why is my predicate not backtracking?

I don't understand why my predicate doesnt backtrack and find all solutions.
person(john).
person(erik).
allExceptSpider(person(Spider),T ):-
setof(person(X),person(X),S),
subtract(S,[person(Spider) ],T).
If I call this predicate with Two variables:
allExceptSpider(person(Z),Q)
Then it will only give me the answer Z = john, Q = [person(erik)]
but it won't backtrack to find Z = erik ,Q = [person(john)]
why?
TL;DR: If you use subtract/3 , your code may lose logical-purity.
person(john).
person(erik).
allExceptSpider(Spider, T) :-
setof(X, person(X), S),
subtract(S, [Spider], T).
Preserve purity! How? Use list_item_subtracted/3 like so:
allExceptSpiderNU(Spider, T) :-
setof(X, person(X), S),
list_item_subtracted(S, Spider, T).
Sample queries head-to-head:
?- allExceptSpider(Z, Q).
Q = [erik], Z = john.
?- allExceptSpiderNU(Z,Q).
Q = [ erik] , Z=john
; Q = [john ], Z=erik
; Q = [john,erik], dif(Z,erik), dif(Z,john).

Prolog: Unification or Backtracking errors in program

I have a simple knowledge base that encodes a family tree. Some important rules in this representation are as follows:
% fathers
father(michael,cathy).
father(michael,sharon).
father(charles_gordon,michael).
father(charles_gordon,julie).
father(charles,charles_gordon).
father(jim,melody).
father(jim,crystal).
father(elmo,jim).
father(greg,stephanie).
father(greg,danielle).
% mothers
mother(melody,cathy).
mother(melody,sharon).
mother(hazel,michael).
mother(hazel,julie).
mother(eleanor,melody).
mother(eleanor,crystal).
mother(crystal,stephanie).
mother(crystal,danielle).
% parents
parent(X,Y) :- father(X,Y).
parent(X,Y) :- mother(X,Y).
% men
male(michael).
male(charles_gordon).
male(charles).
male(jim).
male(elmo).
male(greg).
% women
female(cathy).
female(sharon).
female(julie).
female(hazel).
female(eleanor).
female(melody).
female(crystal).
female(stephanie).
female(danielle).
person(X) :- male(X) ; female(X).
parent(X,Y) :- father(X,Y) ; mother(X,Y). % X is parent of Y
child(X,Y) :- parent(Y,X).
elder(X,Y) :- parent(X,Y). % X is an elder of Y, meaning X is a parent or an ancestor of Y
elder(X,Y) :- parent(X,Z), elder(Z,Y).
junior(X,Y) :- child(X,Y). % X is a junior of Y, meaning X is a child or some descendant of Y
junior(X,Y) :- child(X,Z), junior(Z,Y).
I am attempting to find the nearest elder between two individuals(predicate ne(X,Y,Z)). This individual Z is the elder of both X and Y, and no junior of Z is also an elder of BOTH X and Y.
My attempt looks like this:
ne(X,Y,Z) :- person(X),
person(Y),
X \= Y,
elder(Z,X),
elder(Z,Y),
junior(A,Z),
not(elder(A,X)),
not(elder(A,Y)).
but this is somehow incorrect, because whenever I run ?- ne(stephanie,cathy,Z). i get
Z = jim ;
Z = jim ;
Z = jim ;
Z = jim ;
Z = elmo ;
Z = elmo ;
Z = elmo ;
Z = elmo ;
Z = eleanor ;
Z = eleanor ;
Z = eleanor ;
Z = eleanor ;
but i'm only supposed to get one answer, and I can't figure out what's wrong. Thanks!
from this graph
seems this answer is correct
?- ne(stephanie,cathy,A).
A = eleanor ;
A = jim.
here is my attempt to ne/3
ne(X,Y,Z) :-
setof(A, (
elder(A, X),
elder(A, Y),
X \= Y,
\+ (elder(A, T), elder(T, X) , elder(T, Y) ))
, As), member(Z, As).
not sure it's the better way...
Setof/3 (joined with member/2) is used to eliminate duplicate answers, since we get
?- aggregate(count,A^ne(stephanie,cathy,A),N).
N = 32.
with this core logic
ne(X,Y,A) :-
elder(A, X),
elder(A, Y),
X \= Y,
\+ (elder(A, T), elder(T, X) , elder(T, Y)).
note variable A replaces locally the original Z
edit
I didn't took in account the savvy comment by #Boris, but after removing the duplicate parent/2 definition, the setof/3+member/2 trick become useless.

How to check if any statisfying clause exists in Prolog without backtracking through all different paths?

Let's say I have the following:
parent(alice, charlie).
parent(bob, charlie).
parent(bob, diane).
parent(alice, diane).
parent(bob, eve).
parent(alice, eve).
% people are siblings of each other if they share a parent
% and aren't the same person.
sibling(A, B) :-
parent(X, A),
parent(X, B),
B \= A.
Now if I ask for the siblings of Diane, I get Charlie and Eve — twice, once through Bob and once through Alice. I only want each once.
I don't think I can use a cut here, as that would prevent backtracking altogether. What I would like, is a way to check if any exists.
Paraphrasing
sibling(A, B) :-
∃(parent(X, A), parent(X, B)),
B \= A.
I tried several cuts, but none worked.
I tried findall/3 on (parent(X, A), parent(X, B)) and checking if the resulting list is non-empty, but that doesn't unify A or B.
Using setof/3 as suggested below works, but I really want to find a way to incorporate it in the definition of sibling/2, instead of having to use it in the question. I'd really like to be able to do the following:
?- sibling(diane, X).
X = charlie ;
X = eve ;
false.
or this
?sibling(X, Y).
X = charlie,
Y = diane ;
X = charlie,
Y = eve ;
X = diane,
Y = charlie ;
X = diane,
Y = eve ;
X = eve,
Y = charlie ;
X = eve,
Y = diane ;
false.
Like I said below, I have a solution for this specific case. What I would like, and what I'm setting a bounty for, is a general solution.
Instead of
sibling(A, B) :-
setof(D, X^(parent(X, A), parent(X, D)), Ds),
member(B, Ds),
B \= A.
I'd like to do
sibling(A, B) :-
exists(X^(parent(X, A), parent(X, B))),
B \= A.
which unifies A and B.
How do I define exists/1?
Using cut in Prolog is very delicate. Most cuts are essentially incorrect, but work still in certain situations. You could use a cut here, provided you want exactly one answer. But since you want the entire set, you are out of luck: You need to explore all answers to determine that set.
Fortunately, there is an elegant shortcut (pun intended) for that: setof/3. So ask
?- setof(t, sibling(diane, S), _).
For this usage of setof/3, the last argument is of no interest. It is actually [t].
For a general purpose exists/1, define
exists(XGoal) :- setof(t, XGoal, _).
This allows the use of existential quantifiers.
Here is a version using only the cut predicate !/0:
person(alice).
person(bob).
person(charlie).
person(diane).
person(eve).
parent(alice, charlie).
parent(bob, charlie).
parent(bob, diane).
parent(alice, diane).
parent(bob, eve).
parent(alice, eve).
sibling(X, Y) :-
person(X),
person(Y),
X \= Y,
sameparent(X, Y).
sameparent(X, Y) :-
parent(P, X),
parent(P, Y),
!.
We do not put the predicate !/0 in the predicate sibling/2 to allow backtracking to the goal sibling(X, Y) after the first common parent has been found. Instead we put the predicate !/0 in a new predicate sameparent/2. We also add a person predicate because the goal X \= Y requires its X and Y to be instantiated. See this document for more information.
A query:
?- sibling(diane, Y).
Y = charlie
Y = eve
Another query:
?- sibling(X, Y).
X = charlie,
Y = diane
X = charlie,
Y = eve
X = diane,
Y = charlie
X = diane,
Y = eve
X = eve,
Y = charlie
X = eve,
Y = diane
parent(alice, charlie).
parent(bob, charlie).
parent(bob, diane).
parent(alice, diane).
parent(bob, eve).
parent(alice, eve).
% people are siblings of each other if they share a parent
% and aren't the same person.
sibling(A, B) :-
setof(D, X^(parent(X, A), parent(X, D)), Ds),
member(B, Ds),
B \= A.
?- sibling(X, Y).
X = charlie,
Y = diane ;
X = charlie,
Y = eve ;
X = diane,
Y = charlie ;
X = diane,
Y = eve ;
X = eve,
Y = charlie ;
X = eve,
Y = diane ;
false.
Now I'm wondering how to extract this to a method exists/1, for general use.
So, as seen in the accepted answer, we can run
?- setof(P, (parent(P,diane), parent(P,X), X\=diane), _).
or
?- setof(P, (parent(P,diane), parent(P,X)), _), X\=diane.
to get what you want.
Hence we can define a binary predicate "exists A such that B holds":
exists(A, B):- setof( A, B, _).
and use it as
sibling_v1(A, B):- exists( P, (parent(P,A), parent(P,B)) ), B \= A.
or as
sibling_v2(A, B):- exists( P, (parent(P,A), parent(P,B), B \= A) ).
to define the desired predicates.
But it'll still run through all the possible paths to find out all the possible solutions (just reporting each once). For how could it be certain to have not missed some, otherwise?
57 ?- exists( P, (parent(P,diane), parent(P,X), X\=diane)).
X = charlie ;
X = eve.
58 ?- exists( P, (parent(P,diane), parent(P,X), writeln([P,X]), X\=diane)).
[bob,charlie]
[bob,diane]
[bob,eve]
[alice,charlie]
[alice,diane]
[alice,eve]
X = charlie ;
X = eve.
59 ?-

Resources