Ответ в теме: База даныых: социальные сети

      Комментарии к записи Ответ в теме: База даныых: социальные сети отключены
#3216

Посчитать число друзей (и любые другие параметры) ты можешь только через рекурсию. Другого механизма в прологе просто нет. Счетчик в прологе реализовать нельзя, т.к. у нем нет оператора разрушающего присваивания. Посмотрите «Введение в логическое программирование«.

Другое дело, что через рекурсию это сделать нормально нельзя. Например, вы хотите написать функцию:

friend_count(Who, Count):-
  friend(SocialNetwork, Who, Friend), !,
  friend_count(Who, CountOtherFriend),
  Count = CountOtherFriend + 1.
friend_count(Who, 0).

Эта функция не будет работать — она зациклится даже если в базе данных есть один друг, т.к. при обращении к базе у вас запускается механизм поиска с возвратами, вы находите первое попавшееся решение (первого попавшегося друга) и начинаете рекурсивную обработку. Но в функции, которая была вызвана рекурсивно, поиск с возвратами начинается сначала и вы опять находите того же самого друга.

Чтобы программа не зацикливалась, вам нужно удалять из базы обработанные факты, а на возврате из рекурсивной функции — восстанавливать базу. Это можно сделать примерно так:

friend_count(Who, Count):-
  friend(SocialNetwork, Who, Friend), !,
  retract(friend(SocialNetwork, Who, Friend)),
  friend_count(Who, CountOtherFriend),
  assert(friend(SocialNetwork, Who, Friend)),
  Count = CountOtherFriend + 1.
friend_count(Who, 0).

Мне такое решение не нравится. Во-первых, оно излишне сложное. Во-вторых, если бы вы использовали списки, то возможно было бы реализовать решение с хвостовой рекурсией, т.е. это насколько же эффективное решение, как с использованием циклов в языках типа С++/Java. Изменяя подобным образом базу данных мы никак не сможем использовать хвостовую рекурсию, т.к. после вызова функции нужно восстановить данные в базе.