Традиционный алгоритм DFS включает рекурсию и стек, который по сути является механизмом обратного отслеживания.В Прологе, если вы пытаетесь «накапливать» список, это может быть затруднительно, если необходимые элементы получены при возврате.
Следующий код представляет собой очень простую визуализацию поиска DFS и выпишетузлы в поиске DFS:
dfs(Graph, StartNode) :-
dfs(Graph, StartNode, []).
dfs(Graph, Node, Visited) :-
\+ member(Node, Visited),
member(Node-Neighbors, Graph),
write(Node), nl,
member(NextNode, Neighbors),
dfs(Graph, NextNode, [Node|Visited]).
Два вызова member
немного похожи на то, что вы пытались сделать, но не совсем так.Вместе они являются ключом к обходу DFS для этой структуры.
Запрашивая это, вы получите:
| ?- dfs([0-[1,5], 1-[0,2], 2-[1,3], 3-[2,0], 5-[0]], 0).
0
1
2
3
5
yes
| ?-
Но выписывание узлов в графе не особенно полезно.Мы хотели бы собрать их в список.Выше можно изменить так, чтобы он стал предикатом, который успешно выполняется для каждого узла на пути поиска.
dfs(Graph, StartNode, Node) :-
dfs(Graph, StartNode, [], Node).
dfs(Graph, ThisNode, Visited, Node) :-
\+ member(ThisNode, Visited),
( Node = ThisNode % Succeed finding a new node
; member(ThisNode-Neighbors, Graph), % Or... find the next node
member(NextNode, Neighbors),
dfs(Graph, NextNode, [ThisNode|Visited], Node)
).
В результате:
| ?- dfs([0-[1,5], 1-[0,2], 2-[1,3], 3-[2,0], 5-[0]], 0, Node).
Node = 0 ? a
Node = 1
Node = 2
Node = 3
Node = 5
no
| ?-
Теперь вы можете использовать findall/3
чтобы получить узлы в виде списка:
dfs_path(Graph, StartNode, Nodes) :-
findall(Node, dfs(Graph, StartNode, Node), Nodes).
Теперь вы можете написать:
| ?- dfs_path([0-[1,5], 1-[0,2], 2-[1,3], 3-[2,0], 5-[0]], 0, Nodes).
Nodes = [0,1,2,3,5]
yes
| ?-