鉴于事实:
edges(a,[b,c]).
edges(b,[d]).
edges(c,[a]).
edges(d,[e]).
现在,我可以编写以下谓词:
find(F, L) :-
edges(F, Nodes) ->
findall([X|Y], (member(X, Nodes), find(X, Y)), L);
L = [].
没有循环时工作正常,例如find(b,L)。给了我和我。但是当循环存在时,它不起作用。那么如何修改我的代码来处理循环呢?例如find(c,L)将输出a,b,c,d,e以及find(a,L)。 任何帮助都表示赞赏。
答案 0 :(得分:2)
您可以选择使用累加器来跟踪您访问过的节点。为此,您需要一个列表作为附加参数。由于此列表在搜索开始时为空,因此您始终使用[]
调用谓词,因此您也可以使用调用谓词隐藏它,我们可以将其称为start_dest/2
:< / p>
start_dest(S,D) :-
dif(S,D), % start and destination nodes are different
start_dest_(S,D,[]). % actual relation called with empty accumulator
第一个目标dif/2
仅用于防止起始节点和目标节点相同的解决方案。如果您想允许此类解决方案,请删除该目标。实际关系将通过逐节点遍历图来搜索可到达的节点。你可以区分两种情况。
如果两个节点相等,则找到可能的目标节点。
如果节点不同,则必须是您当前所在节点的邻接列表中的中间节点。到目前为止,搜索中不得访问当前节点(以避免循环)。必须有从中间节点到目的地的路径,并且当前节点不得出现在该路径中,因此必须将其添加到访问节点列表中。
你可以在Prolog中表达这两个案例,如下:
start_dest_(D,D,_Visited). % case 1: destination found
start_dest_(S,D,Visited) :- % case 2:
maplist(dif(S),Visited), % S has not been visited yet
edges(S,Reachable), % Reachable is the adjacence list
member(X,Reachable), % that has to contain the intermediate node X
start_dest_(X,D,[S|Visited]). % there has to be a path from X to D that
% does not include S
您的示例查询会产生所需的结果:
?- start_dest(b,N).
N = d ;
N = e ;
false.
?- start_dest(c,N).
N = a ;
N = b ;
N = d ;
N = e ;
false.
如果删除dif(S,D)
中的第一个目标(start_dest/2
),则会获得其他解决方案。这对应于每个节点都可以从自身访问的视图。
?- start_dest(b,N).
N = b ;
N = d ;
N = e ;
false.
请注意,此谓词可用于所有方向,例如哪些节点可以e
到达?:
?- start_dest(S,e).
S = a ;
S = b ;
S = c ;
S = d ;
false.
或最常见的查询:哪些节点可以从任何节点访问?:
?- start_dest(S,D).
S = a,
D = b ;
S = a,
D = d ;
S = a,
D = e ;
S = a,
D = c ;
S = b,
D = d ;
S = b,
D = e ;
S = c,
D = a ;
S = c,
D = b ;
S = c,
D = d ;
S = c,
D = e ;
S = d,
D = e ;
false.
与谓词find/2
相反,start_dest/2
一次为您提供一个可达节点。如果要获取列表中的所有可访问节点,可以像findall/3
中一样使用bagof/3
,setof/3
和find/2
等谓词,例如:
?- bagof(N, start_dest(b,N), Reachable).
Reachable = [d, e].
?- bagof(N, start_dest(c,N), Reachable).
Reachable = [a, b, d, e].
如果您打算始终搜索所有可到达的节点但又不想一直查询bagof/3
,则可以编写一个调用谓词,如:
reachable_from(Reachable,Start) :-
bagof(N, start_dest(Start,N), Reachable).
?- reachable_from(Reachable,Start).
Reachable = [b, d, e, c],
Start = a ;
Reachable = [d, e],
Start = b ;
Reachable = [a, b, d, e],
Start = c ;
Reachable = [e],
Start = d.
答案 1 :(得分:0)
这是一种可能性:
% we get a list of all edges
get_all_edges(Edges) :-
bagof(edges(X,Y), edges(X,Y), Edges).
% main predicate
find(F, L) :-
get_all_edges(Edges),
find(Edges, F, Out),
% the result you get is for example [[a, [b, [d, [e|e], [e]]], [c]]]
flatten(Out, FOut),
list_to_set(FOut, L).
% no more edges, work is done
find([], L, L).
find(Edges, F, L) :-
% we get the good nodes
select(edges(F, Nodes), Edges, Rest)
-> findall([X|Y], (member(X, Nodes), find(Rest, X, Y)), L)
; L = [].
结果:
?- find(c, L).
L = [a, b, d, e, c].
答案 2 :(得分:0)
在您学会了基本方法后,请查看libraries提供的内容:
?- findall(V-U,(edges(V,Us),member(U,Us)),Es),
vertices_edges_to_ugraph([],Es,G),
reachable(a,G,Rs).
Es = [a-b, a-c, b-d, c-a, d-e],
G = [a-[b, c], b-[d], c-[a], d-[e], e-[]],
Rs = [a, b, c, d, e].
您可能想直接从edge / 2转到ugraph格式,但最好使用预定义的功能(即vertices_edges_to_ugraph / 3)