我正在Prolog(gprolog)中编写一个程序在图表上进行路径查找。我将其部分基于this SO post。
让我举个例子。以下是我绘制的示例图:
以下是代码最初的样子:
path([Goal],Goal,Goal).
path(P,Start,Goal) :- adjacent(Start,X),
\+ (memberchk(X,P)),
(
Goal=X
;
path([Start|P],X,Goal)
).
无论基本情况是否多余,这都是问题所在。我想要一个
的输入风格| ?- path(P,a,f).
对于那个输入,我会得到
的输出P = [a,s,f]
true ?
然而,代码的问题在于memberchk
。对于memberchk(a,P)
,它会尝试统一,调用memberchk(a,[a|_])
,然后返回true。我不希望发生这种情况,因此我首先检查P
是否使用var/1
谓词进行实例化。因此,我的代码改为
path([Goal],Goal,Goal).
path(P,Start,Goal) :- var(P),
path([],Start,Goal).
path(P,Start,Goal) :- adjacent(Start,X),
\+ (memberchk(X,P)),
(
Goal=X
;
path([Start|P],X,Goal)
).
现在,如果P
未实例化,我们会使用空列表调用path/3
。 我的问题是:现在我无法打印P
,因为我致电path([],Start,Goal)
而P
已不再与[]
相关联。
我已尝试使用write/1
谓词,但它在每一步打印出P
或打印P = _26
(意味着它打印未经实例化的P
,而不是最终价值P
)。
我希望这是一个简单的问题,我对Prolog非常陌生。
如果有类似问题,请道歉;我很乐意指出其他可以提供帮助的问题。在发布之前我搜索了SO和Google。
答案 0 :(得分:2)
您需要的概念是累加器
你实际上非常接近:你意识到确实将P
初始化为[]
,并在你递归时用[Start|P]
填充它是一个有效的策略。这称为累加器,要获得最终结果,您只需要添加另一个参数。
以下是您查询的新path/3
谓词:
path(P, Start, Goal) :-
path([], P, Start, Goal).
正如您所看到的,我们在此处添加[]
作为path/4
的第一个参数,我们将这样实现:
path(L, P, Goal, Goal) :-
reverse([Goal|L], P).
path(L, P, Start, Goal) :-
adjacent(Start, X),
\+ (memberchk(X, L)),
path([Start|L], P, X, Goal).
第一个子句用于终止递归。一旦Start
和Goal
参数与您指出的相同,递归应该结束。使用累加器时,这意味着我们将累加器与输出参数统一起来。但是,累加器包含相反的答案(并且没有最终目标),所以我们有reverse([Goal|L], P)
。
第二个子句与您编写的内容非常相似,但我们现在需要将P
原样传递给recursive子句。请注意,我已删除了您在该条款中的分离,在这种情况下不需要。
完整的代码:
path(P, Start, Goal) :-
path([], P, Start, Goal).
path(L, P, Goal, Goal) :-
reverse([Goal|L], P).
path(L, P, Start, Goal) :-
adjacent(Start, X),
\+ (memberchk(X, L)),
path([Start|L], P, X, Goal).
答案 1 :(得分:0)
我解决了我的问题。解决方案依赖于:
我的代码如下:
connected(X,Y) :- adjacent(X,Y);adjacent(Y,X).
not_member(_, []).
not_member(X, [Head|Tail]) :- X \== Head, not_member(X, Tail).
path(P,A,B):-path_helper(P,A,B,[Start]).
path_helper([X,Y],X,Y,_):-connected(X,Y).
path_helper([Goal],Goal,Goal,_).
path_helper([Start|[Head|P]],Start,Goal,Visited):-connected(Start,Head),not_member(Head,Visited),path_helper([Head|P],Head,Goal,[Head|Visited]).