Вы близки, но синтаксис списка, кажется, немного смущает вас. И, к сожалению, ваш предикат add
не помогает.
Давайте попробуем понять, что означает add
в двух случаях, когда вы его используете. В первом пункте path
у вас есть цель add(T, [], [H|Hs])
. Мы можем спросить Пролога, что это значит:
?- add(T, [], [H|Hs]).
T = H,
Hs = [].
Таким образом, первое предложение path
может применяться, только если Hs
является пустым списком (следовательно, [H|Hs]
совпадает с [H]
), а T
совпадает с H
. Поэтому мы можем переписать это предложение так:
path(T, [T]) :-
prereqs(T, []).
Это выглядит разумно, и на самом деле это прямой перевод на пролог соответствующей части спецификации:
Если задача T не имеет предварительных условий, то существует единственный путь к ней, заданный списком [T].
Теперь давайте попробуем то же самое для использования add
во втором предложении path
. Он принимает следующую форму:
?- add(T, [], Hs).
Hs = [T].
Упрощение второго предложения path
, как указано выше, с использованием этого уравнения приводит к:
path(T, [H, T]) :-
prereqs(T, [N|_]),
path(N, H).
Обратите внимание, что вторым аргументом в заголовке является [H, T]
, поскольку [H|Hs]
= [H|[T]]
= [H, T]
. Так что это может быть успешным только для двухэлементных списков, что вы и видите. (Первым элементом каждого из этих списков может быть другой вложенный двухэлементный список.)
Вместо этого вы ищете нечто большее:
path(T, Path) :-
prereqs(T, [N|_]),
path(N, PathToPrerequisite),
append(PathToPrerequisite, [T], Path).
Это все еще имеет ошибку в том, что он не может найти все пути, но он может, по крайней мере, уже генерировать те, которые он нашел, в правильном формате:
?- path(f, Path).
Path = [b, c, f] ;
false.