Здесь задействованы три элемента: поиск с возвратом, унификация и нотация списка. Я объясню три на более простом примере:
moon(europa).
moon(ganymede).
planet(jupiter).
planet(saturn).
Мы знаем, что Европа и Ганимед — две луны (Юпитера), а Юпитер и Сатурн — планеты. Когда мы спрашиваем, какие планеты известны, мы пишем:
?- planet(X).
X = jupiter ; % type ; for the next answer
X = saturn. % there's no more answer, hence .
Унификация происходит, когда пролог ищет заголовок правила, который соответствует запросу, в котором переменные заменяются соответствующим образом. Например, нет замены, которая делает moon(X) = planet(Y)
равным, но есть замена для planet(jupiter) = planet(X)
, а именно X=jupiter
. Вот как вы получаете первое решение. Для второго решения Пролог должен унифицироваться со вторым заголовком правила, а именно planet(saturn) = planet(X)
. Поскольку это делается после полного перечисления первого варианта, мы называем это возвратом.
Теперь мы можем сосредоточиться на (связанных) списках. Список либо пуст ([]
), либо имеет первый элемент X
, добавленный к хвостовому списку Xs
([X|Xs]
). В Прологе для списка [X | [Y | [] ]]
также имеется более удобное обозначение, а именно [X,Y]
. Внутренне они одинаковы. Теперь, когда мы хотим составить список астральных объектов, мы можем сформулировать следующие три правила:
astral_objects([]). % The empty list is a list of astral objects.
astral_objects([X|Xs]) :- % The list [X | Xs] is a list of astral objects if...
moon(X), % ... its first element X is a moon
astral_objects(Xs). % ... and the remaining list Xs is a list of astral objects
astral_object([X|Xs]) :- % Likewise for planets
planet(X),
astral_objects(Xs).
Когда мы формулируем запрос для двухэлементного списка, мы получаем все комбинации объектов:
?- astral_object([A,B]).
A = B, B = europa ;
A = europa,
B = ganymede ;
A = europa,
B = jupiter ;
A = europa,
B = saturn ;
A = ganymede,
B = europa ;
A = B, B = ganymede ;
A = ganymede,
B = jupiter
%...
При объединении применяются только правила 2 и 3. В обоих случаях у нас есть astral_objects([X|Xs]) = astral_objects([A,B])
. Помните, что [A,B]
— это сокращение от [A|[B]]
, а также X=A
и Xs=[B]
. Первое правило тела объединит X
с соответствующей луной/планетой, а шаг рекурсии описывает хвост. Мы снова объединяем astral_objects([X|Xs]) = astral_objects([B])
, что приводит к X=B
и Xs = []
. Теперь шаг рекурсии будет соответствовать только терминальному случаю пустого списка, и мы полностью изучили этот путь.
Что произойдет, если мы найдем произвольный список астральных объектов?
?- astral_object(Xs).
Xs = [] ;
Xs = [europa] ;
Xs = [europa, europa] ;
Xs = [europa, europa, europa] ;
Xs = [europa, europa, europa, europa] ;
Xs = [europa, europa, europa, europa, europa]
%... does not terminate
Голова astral_objects(Xs)
соответствует всем трем телам. После возврата замены для терминального падежа он снова и снова спускается к первому правилу. Поскольку длина списка не ограничена, существует бесконечное количество решений, которые нужно найти, прежде чем будет применено третье правило. Чтобы избежать этого, вы можете честно перечислить списки, прежде чем пытаться заставить их удовлетворять предикату:
?- length(Xs,_), astral_object(Xs).
Xs = [] ;
Xs = [europa] ;
Xs = [ganymede] ;
Xs = [jupiter] ;
Xs = [saturn] ;
Xs = [europa, europa] ;
Xs = [europa, ganymede] ;
Xs = [europa, jupiter] ;
Xs = [europa, saturn] ;
Xs = [ganymede, europa]
%...
Он по-прежнему не завершается, но вы видите списки по возрастанию длины и, следовательно, разнообразие.
person
lambda.xy.x
schedule
04.12.2017
!
). - person Willem Van Onsem   schedule 03.12.2017getConspirators([H|T], CConspirators, Res):-
была вырезана! перед его рекурсивным вызовом (имеет ли значение, где вы его поместите?) он остановит выполнение кода там, если что-то выйдет из строя во время выполнения, и перейдет к следующему предложению (getConspirators([_|T], CConspirators, Res) :-
)? Но без разреза (как здесь) последний пункт будет запускаться каждый раз после первого, независимо от того, что происходит? Итак, если все это правильно, задача второго предложения состоит в том, чтобы просто перейти к следующему элементу в списке (например, цикл for)? - person Schytheron   schedule 03.12.2017getConspirators([H|T], CConspirators, Res)
терпят неудачу до достижения разреза!
, разрез не будет выполнен. Если вы выполните отсечение, вы не будете учитывать оставшиеся предложения этого предиката в этом рекурсивном вызове (ни глобально, ни в рекурсивных вызовах, исходящих из этого вызова). - person Willem Van Onsem   schedule 03.12.2017getConspirators([_|T], CConspirators, Res) :-
(последнего). Почему это там? - person Schytheron   schedule 03.12.2017