0
votes

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 ?

4
Can you post also more input data to test the program? Also note that the program you posted doesn't work (likes/3 and member_/2 does not exist).damianodamiano
1) What is G representing ?? 2) For what X is supposed friendly(G, X) to succeed for G = [person(alice,[bob]),person(bob,[])] ??coder
Sorry, I added the missing code and edited a bit.vs2010noob
Is it possible you have misunderstood the instructions? The person/2 clauses should probably be in the database, not passed in a list.Tomas By
And please post code, not pictures.Tomas By

4 Answers

1
votes

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.

1
votes

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
0
votes

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.

0
votes

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).