Для предиката, который находит друзей на заданном расстоянии, как вы просили в комментариях, вам понадобятся три аргумента, то есть два друга и расстояние. Давайте дадим ему хорошее реляционное имя, скажем friend_of_maxdist/3
. Теперь давайте попробуем описать отношение:
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
Этот предикат доставляет всех друзей на заданном расстоянии один за другим:
?- 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.
Теперь вы можете собрать все решения в списке. Я покажу примеры запросов с bagof / 3 , см. Ниже ниже причину, по которой:
?- 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].
Однако из-за использования >/2
и is/2
, friend_of_maxdist/3
выдает ошибку, если третий аргумент не является обоснованным, например, для запроса:
?- friend_of_maxdist(a,F2,N).
ERROR: >/2: Arguments are not sufficiently instantiated
Если вы не собираетесь использовать предикат таким образом, все готово. В противном случае вы могли бы взглянуть на CLP (FD) . Сделайте следующие изменения в приведенном выше коде:
:- 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).
Если вы попробуете проблемный запрос сейчас, вы получите ответы вместо ошибки. Тем не менее, вы получите остаточные цели (см. документация для деталей) в ответе:
?- 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.
Чтобы получить действительные числа вместо диапазонов для N
, ограничьте его диапазон и назовите его:
?- 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.
Теперь вы можете собирать решения, как указано выше:
?- 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].
В приведенном выше запросе вы можете увидеть, почему я предложил bagof/3
для сбора решений: N
привязан к значению, а затем вы получите все решения по этому значению. Если вы попробуете то же самое с findall/3
, вы получите все элементы трех списков в одном списке:
?- 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|...].
Чтобы получить то же решение с bagof/3
, вы должны явно указать bagof/3
не связывать N
в цели:
?- 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|...].
Обратите внимание, что CLP (FD) -версия предиката теперь напоминает истинное отношение, как предполагает его реляционное имя.