Path Prolog中的寻路

Path Prolog中的寻路,path,prolog,path-finding,Path,Prolog,Path Finding,我试着自学序言。下面,我写了一些代码,我认为应该返回无向图中节点之间的所有路径。。。但事实并非如此。我试图理解为什么这个特定的代码不起作用(我认为这与类似的Prolog寻路帖子不同)。我在SWI Prolog中运行这个。有什么线索吗 % Define a directed graph (nodes may or may not be "room"s; edges are encoded by "leads_to" predicates). room(kitchen). room(living_r

我试着自学序言。下面,我写了一些代码,我认为应该返回无向图中节点之间的所有路径。。。但事实并非如此。我试图理解为什么这个特定的代码不起作用(我认为这与类似的Prolog寻路帖子不同)。我在SWI Prolog中运行这个。有什么线索吗

% Define a directed graph (nodes may or may not be "room"s; edges are encoded by "leads_to" predicates).
room(kitchen).
room(living_room).
room(den).
room(stairs).
room(hall).
room(bathroom).
room(bedroom1).
room(bedroom2).
room(bedroom3).
room(studio).
leads_to(kitchen, living_room).
leads_to(living_room, stairs).
leads_to(living_room, den).
leads_to(stairs, hall).
leads_to(hall, bedroom1).
leads_to(hall, bedroom2).
leads_to(hall, bedroom3).
leads_to(hall, studio).
leads_to(living_room, outside).  % Note "outside" is the only node that is not a "room"
leads_to(kitchen, outside).

% Define the indirection of the graph.  This is what we'll work with.
neighbor(A,B) :- leads_to(A, B).
neighbor(A,B) :- leads_to(B, A).
如果A-->B-->C-->D是无循环路径,则

path(A, D, [B, C])
应该是真的。即,第三个参数包含中间节点

% Base Rule (R0)
path(X,Y,[]) :- neighbor(X,Y).

% Inductive Rule (R1)
path(X,Y,[Z|P]) :- not(X == Y), neighbor(X,Z), not(member(Z, P)), path(Z,Y,P).
然而

这是错误的。为什么?我们不应该和R1有一个匹配吗

X = bedroom1
Y = stairs
Z = hall
P = []
既然

?- neighbor(bedroom1, hall).
true.

?- not(member(hall, [])).
true.

?- path(hall, stairs, []).
true .
?

事实上,如果我评估

?- path(A, B, P).

我只得到长度为1的解决方案。

欢迎使用Prolog!本质上,问题在于,当您在R1中到达
not(成员(Z,P))
时,
P
仍然是一个纯变量,因为求值尚未到达
路径(Z,Y,P)
来定义它。Prolog令人惊讶但鼓舞人心的一点是,
member(Ground,Var)
将生成包含
Ground
的列表,并将它们与
Var
统一起来:

?- member(a, X).
X = [a|_G890] ;
X = [_G889, a|_G893] ;
X = [_G889, _G892, a|_G896] .
这会产生令人困惑的副作用,即检查未实例化列表中的值总是会成功,这就是为什么
not(member(Z,p))
总是会失败,导致R1总是失败。事实上,您得到了所有的R0解决方案,而没有一个R1解决方案,这表明R1中的某些东西总是导致它失败。毕竟,我们知道R0是有效的

如果你交换这两个目标,你会得到你想要的第一个结果:

path(X,Y,[Z|P]) :- not(X == Y), neighbor(X,Z), path(Z,Y,P), not(member(Z, P)).

?- path(bedroom1, stairs, P).
P = [hall]
如果您要求另一种解决方案,您将得到堆栈溢出。这是因为在更改之后,我们很高兴地使用
路径(Z,Y,P)
以循环的方式尽快生成解决方案,但在事后使用
而不是(成员(Z,P))
放弃它们。(顺便说一句,为了稍微提高效率,我们可以切换到
memberchk/2
,而不是
member/2
。当然,更快地做错事没有多大帮助。:)

我倾向于将其转换为广度优先搜索,这在Prolog中意味着添加一个“开集”参数来包含您尚未尝试过的解决方案,并在每个节点上首先尝试开集中的某个内容,然后将该节点的可能性添加到开集的末尾。当开放集熄灭时,您已经尝试了可以到达的每个节点。对于某些路径查找问题,无论如何,它是比深度优先搜索更好的解决方案。您可以尝试的另一件事是将路径分为已访问组件和未来组件,并且只检查已访问组件。只要在当前步骤中没有生成循环,就可以确定根本没有生成循环,不必担心将来的步骤

你对问题的措辞让我相信你不想要一个完整的解决方案,只是一个暗示,所以我认为这就是你所需要的。如果不正确,请告诉我

path(X,Y,[Z|P]) :- not(X == Y), neighbor(X,Z), path(Z,Y,P), not(member(Z, P)).

?- path(bedroom1, stairs, P).
P = [hall]