Добро пожаловать на Прологе!
Сначала вы будете нуждаться некоторые факты:
friend(john, tom).
friend(tom, lucy).
friend(lucy, ben).
...
Для простоты рассмотрим случай, когда направлены дружба: Я могу друг вас, но это не означает, что вы меня другу.
Предположим, что мы друзья степени 1, если я вас поддержал. Это будет выглядеть так:
network(Person, 1, Friend) :- friend(Person, Friend).
Теперь индуктивный случай - это тот, в котором мы нашли друга через друга. Это будет выглядеть так:
network(Person, N1, FoaF) :-
N1 > 1,
N0 is N1-1,
network(Person, N0, Friend),
network(Friend, 1, FoaF).
Использование is/2
вы можете быть уверены, что предикат будет плохо себя вели. Например, если вы опустите ограничение > 1
, вы сможете задать вопросы и получить N обратно, если вы его не включите. Но вы также получите ошибки в отсутствии локального стека. Так что если вы можете позволить себе, привести в clpfd
Сейчас:
:- use_module(library(clpfd)).
network(Person, 1, Friend) :- friend(Person, Friend).
network(Person, N1, FoaF) :-
N1 #> 0,
N0 #= N1-1,
network(Person, N0, Friend),
network(Friend, 1, FoaF).
Это должно работать для всех случаев ввода вы заботитесь, чтобы попробовать, хотя она по-прежнему не имеет никакого способа знать, когда вы из уровней дружбы.
?- network(john, N, X).
N = 1,
X = tom ;
N = 2,
X = lucy ;
N = 3,
X = ben ;
^CAction (h for help) ? abort
% Execution Aborted
?- network(john, 3, X).
X = ben ;
false.
?- network(john, 2, X).
X = lucy ;
false.
Редактировать Позвольте мне ответить на ваши вопросы по порядку.
Где печать?
По дизайну, я не использовал его.До сих пор мы просто используем Prolog REPL (цикл чтения-eval-print) для ввода ввода-вывода. Это естественный способ работы с Prolog. Позднее вы избавитесь от боли, если у вас возникнут проблемы с разделением предикатов, которые делают ввод-вывод и презентацию пользователя из предикатов, имеющих значение. Это всего лишь небольшое применение разделения модели. Вы также можете сохранить побочные эффекты на карантине в своих собственных предикатах. Пока чистая логическая часть вашей программы является самодостаточной, вы всегда сможете создавать и компоновать ее.
Как вы можете печатать до определенного количества людей. например если вы введете сеть (john, 3, X). то он должен распечатываться до N = 3 X = ben
Я бы склонен называть этот предикат show_network/2
вместо этого и продолжать разделение. Вы можете сделать это по дешевке с отказом управляемой петли следующим образом:
show_network(Person, Max) :-
between(1, Max, N),
network(Person, N, Friend),
format('~w is friends with ~w\n', [Person, Friend]),
fail.
show_network(_, _).
Это будет работать следующим образом:
?- show_network(john, 3).
john is friends with tom.
john is friends with lucy.
john is friends with ben.
true.
Там находятся и другие подходы, например, вы могли бы использовать forall/2
:
show_network(Person, Max) :-
forall(
(between(1, Max, N), network(Person, N, Friend)),
format('~w is friends with ~w\n', [Person, Friend])).
Взаимосвязь между циклом, управляемым отказами, и который должен быть довольно ясным. Вы также можете вручную получить список, а затем обработать его maplist/2
или что-то:
show_network(Person, Max) :-
findall(friend(Person,Friend),
(between(1, Max, N), network(Person, N, Friend)),
Friends),
maplist(show_friend, Friends).
show_friend(friend(Person, Friend)) :-
format('~w is friends with ~w\n', [Person, Friend]).
Спасибо, это очень подробно и очень полезно. У меня мало вопросов, как вы будете печатать до определенного количества людей. например если вы введете сеть (john, 3, X). то он должен распечатать до N = 3 X = ben, из приведенного выше примера. а также то, что означает FoaF, N0, use_module (library (clpfd)). Кстати, еще раз спасибо, что это очень подробно. – KennD
Также где печатная заявка? – KennD
Я добавил некоторое обсуждение ваших вопросов к ответу, пожалуйста, взгляните. –