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.