0

I'm struggling to get a decent result,

I have some friends,

friend(a,b).
friend(a,b2).
friend(a,b3).
friend(b,c).
friend(c,d).
friend(d,e).  
friend(e,f).

Using findall(X,friend(a,X),List) I'm getting all direct friends of a

List=[b,b2,b3].

For example, I want to get a list of 3 level friends of a, for example, I want direct friends of a, the friends of friends of a (it means friends of b, b2, b3) and the friends of c. Getting the list:

List=[b,b2,b3,c,d].

I'm trying everything. I can only get direct friends or all friends of friends.

Help!!

repeat
  • 18,496
  • 4
  • 54
  • 166
  • Define a predicate `friend_of_friend_of_friend` to model your "level 3 friends" relation and use `findall` on that. That makes the relation testable in isolation, it makes the `findall` call simpler, and it doesn't involve the temporary variables that are causing you trouble. – Isabelle Newbie May 09 '18 at 13:03
  • You need to define a predicate that defines the relationship between "indirect" friends. It would be a recursive rule. If you need to limit the "depth", you can include an argument (a count) that constrains how deep the recursion goes. – lurker May 09 '18 at 13:14

2 Answers2

4

For a predicate that finds friends up to a given distance, as you've asked for in the comments, you'll need three arguments, that is the two friends and the distance. Let's give it a nice relational name, say friend_of_maxdist/3. Now let's try to describe the relation:

friend_of_maxdist(F1,F2,D) :-
   D > 0,                        % if the distance is greater than 0
   friend(F1,F2).                % F2 is a friend in range
friend_of_maxdist(F1,F2,D) :-
   D > 1,                        % if the distance is greater than 1
   D0 is D-1,                    
   friend(F1,X),                 % X is an intermediary friend
   friend_of_maxdist(X,F2,D0).   % of distance minus 1

This predicate delivers all friends in the given distance one by one:

?- friend_of_maxdist(a,F2,1).
F2 = b ;
F2 = b2 ;
F2 = b3 ;
false.

?- friend_of_maxdist(a,F2,2).
F2 = b ;
F2 = b2 ;
F2 = b3 ;
F2 = c ;
false.

?- friend_of_maxdist(a,F2,3).
F2 = b ;
F2 = b2 ;
F2 = b3 ;
F2 = c ;
F2 = d ;
false.

Now you can collect all solutions in a list. I'll show the example queries with bagof/3, see further below for the reason why:

?- bagof(F2,friend_of_maxdist(a,F2,1),L).
L = [b, b2, b3].

?- bagof(F2,friend_of_maxdist(a,F2,2),L).
L = [b, b2, b3, c].

?- bagof(F2,friend_of_maxdist(a,F2,3),L).
L = [b, b2, b3, c, d].

However, due to the use of >/2 and is/2, friend_of_maxdist/3 yields an error if the third argument is not ground, e.g. for the query:

?- friend_of_maxdist(a,F2,N).
ERROR: >/2: Arguments are not sufficiently instantiated

If you don't intend to use the predicate this way, you're done. Otherwise you might like to take a look at CLP(FD). Make the following alterations to the above code:

:- use_module(library(clpfd)).   % <- new

friend_of_maxdist(F1,F2,D) :-
   D #> 0,                       % <- change
   friend(F1,F2).
friend_of_maxdist(F1,F2,D) :-
   D #> 1,                       % <- change
   D0 #= D-1,                    % <- change
   friend(F1,X),
   friend_of_maxdist(X,F2,D0).

If you try the problematic query now, you get answers instead of an error. However, you get residual goals (see documentation for details) in the answer:

?- friend_of_maxdist(a,F2,N).
F2 = b,
N in 1..sup ;
F2 = b2,
N in 1..sup ;
F2 = b3,
N in 1..sup ;
F2 = c,
N in 2..sup,
_G778+1#=N,
_G778 in 1..sup ;
F2 = d,
N in 3..sup,
_G1264+1#=N,
_G1264 in 2..sup,
_G1288+1#=_G1264,
_G1288 in 1..sup ;
F2 = e,
N in 4..sup,
_G1855+1#=N,
_G1855 in 3..sup,
_G1879+1#=_G1855,
_G1879 in 2..sup,
_G1903+1#=_G1879,
_G1903 in 1..sup ;
F2 = f,
N in 4..sup,
_G2446+1#=N,
_G2446 in 4..sup,
_G2470+1#=_G2446,
_G2470 in 3..sup,
_G2494+1#=_G2470,
_G2494 in 2..sup,
_G2518+1#=_G2494,
_G2518 in 1..sup ;
false.

To get actual numbers instead of ranges for N, restrict its range and label it:

?- N in 0..3, friend_of_maxdist(a,F2,N), label([N]).
N = 1,
F2 = b ;
N = 2,
F2 = b ;
N = 3,
F2 = b ;
N = 1,
F2 = b2 ;
N = 2,
F2 = b2 ;
N = 3,
F2 = b2 ;
N = 1,
F2 = b3 ;
N = 2,
F2 = b3 ;
N = 3,
F2 = b3 ;
N = 2,
F2 = c ;
N = 3,
F2 = c ;
N = 3,
F2 = d ;
false.

Now you can collect the solutions like above:

?- bagof(F2,(N in 0..3, friend_of_maxdist(a,F2,N), label([N])),L).
N = 1,
L = [b, b2, b3] ;
N = 2,
L = [b, b2, b3, c] ;
N = 3,
L = [b, b2, b3, c, d].

In the above query you can see why I suggested bagof/3 for collecting the solutions: N is bound to a value and then you get all solutions with respect to that value. If you try the same with findall/3, you get all elements of the three lists in one list:

?- findall(F2,(N in 0..3, friend_of_maxdist(a,F2,N), label([N])),L).
L = [b, b, b, b2, b2, b2, b3, b3, b3|...].

To get the same solution with bagof/3, you have to explicitly tell bagof/3 not to bind N in the goal:

?- bagof(F2,N^(N in 0..3, friend_of_maxdist(a,F2,N), label([N])),L).
L = [b, b, b, b2, b2, b2, b3, b3, b3|...].

Note that the CLP(FD)-version of the predicate now resembles a true relation, as its relational name suggests.

tas
  • 8,100
  • 3
  • 14
  • 22
1

The following stands for a 3-level friendship. As said in comments, if you want more, you might want to look for recursion.

% my friend is my friend
friend_of_friend(X,Y):-
    friend(X,Y).
% the friend of my friend is my friend
friend_of_friend(X,Y):-
    friend(X,Z),
    friend(Z,Y).
% the friend of the friend of my friend is my friend
friend_of_friend(X,Y):-
    friend(X,A),
    friend(A,B),
    friend(B,Y).

Then

findall(X, friend_of_friend(a,X), List).

Gives:

List = [b, b2, b3, c, d]

This would stand for infinite recursive friendship:

recursive_friend(X,Y):-
    friend(X,Y).
recursive_friend(X,Y):-
    friend(X,Z),
    recursive_friend(Z,Y).

And give:

List = [b, b2, b3, c, d, e, f]
Rafalon
  • 4,450
  • 2
  • 16
  • 30
  • thanks a lot. But if u can help me in just one more thing. i was looking to do something like the infinite recursive part, but i want a generic rule that for given 'N' make the recursion stop. for example for N=0 -> List=[] , for N=1 -> direct friends. Im trying to use accumulators for stopping the recursion but no success. – Daniel Fialho May 09 '18 at 14:40
  • You could use something like `recursive_friend(X,Y,N)` and inside the predicate use `N > 0, M is N - 1` and change the recursive call for `recursive_friend(Z,Y,M)` - edit: I just looked at tas' answer and it's similar to what he's done – Rafalon May 12 '18 at 16:14