我有以下事实和规则:
flight(sea,msp).
flight(msp,jfk).
route(A,B) :- flight(A,B).
route(B,A) :- flight(A,B).
route(A,C) :- flight(A,B) , flight(B,C).
查询route(sea,jfk)
时我得到的结果为true
,但我希望得到的是探索:
sea-->msp-->jfk
这样我不仅可以说明它是真的,还可以告诉它是如何真实的。
答案 0 :(得分:3)
所以你想从A
到B
,但不仅如此,你还想知道行程的电台列表。
请务必仔细查看以下两个相关问题以及针对该问题提出的答案:
上面链接中提供的元谓词允许您将递归处理委托给可靠,经过测试,可重用的组件。有更多时间专注于解决问题的其他部分!
答案 1 :(得分:2)
您可以跟踪图表中已访问过的节点。无论如何,你需要这样做,因为你需要在图中检测 cycle ,以免陷入无限递归的兔子洞。
在Prolog中,我们使用 helper 方法将状态作为一个或多个额外参数。一个经常使用的约定是有一个“公共”谓词 - 比如route/3
,它调用一个具有更高arity的同名的“私有”工作者谓词,比如说route/4
。这样的事应该对你有用:
route( A , B , R ) :- % find a route R from A to B
route(A,B,[],R) % - by invoking a worker, seeding its list of visited nodes with the empty list
. % Easy!
route(B,B,V,R) :- % we've arrived at the destination (B) when the origination node is the same as the destination node.
reverse([B|V],R) % - just reverse the list of visited nodes to get the routing.
. %
route(A,B,V,R) :- % otherwise...
flight(A,T) , % - if there's an edge out of the current node (A) ,
\+ member(T,V) , % - to an as-yet unvisited node...
route(T,B,[A|V],R) % - go visit that node, marking the current node as visited.
. % Easy!
答案 2 :(得分:1)
这在很大程度上取决于你的prolog系统。正如您已将其标记为swi,我将为您提供SWI特定的答案。
您可以启动跟踪器。使用trace/0
:
?: trace.
true
[trace]?:
现在输入查询时,您可以看到谓词的所有调用,退出,失败和重做。但是,您无法在命令行跟踪器中看到变量名称。要查看您可以执行的操作,可以键入h
。最有趣的可能是下一步n
和f
完成当前目标。
或者也可以使用trace/1
和trace/2
来输出部分调用堆栈:
?: trace(flight/2). % calls, exits, fails and redos are output for flight/2
?: trace(route/2, +exit). % only exits are output for route/2.
如果您还安装了xpce,则可以使用gtrace/0
作为图形界面。
如果你想从prolog中访问你的路线,你也可以写一个新的route/3
,也输出一个方法列表。
因此,对于您的情况,您可以执行以下查询:
?- trace(flight/2,+exit).
% flight/2: [exit]
true.
[debug] ?- route(sea,jfk).
T Exit: (7) flight(sea, msp)
T Exit: (7) flight(msp, jfk)
true.
答案 3 :(得分:1)
如果您既不想使用调试功能,也不想使用辅助谓词编写其他方法,则第三个选择是利用SWI-Prolog的许多内置功能进行元编程。在这种情况下,clause/2
predicate可能会有所帮助(它是ISO标准的一部分,因此其他Prolog方言也可能有,但是我没有检查):
clause(:Head,?Body)
如果
Head
可以与子句标题统一而Body
与相应的子句主体统一,则为真。提供关于回溯的替代条款。实际上,Body
与原子true
统一。
所以我们可以写一个通用谓词expl(+Goal,-Expl)
来构造Goal
的“解释”,以防Goal
成功:
flight(sea,msp).
flight(msp,jfk).
route(A,B) :- flight(A,B).
route(B,A) :- flight(A,B).
route(A,C) :- flight(A,B) , flight(B,C).
% construct an explanation for a solution
expl([],[]).
expl([Goal|Goals],[(Goal,BodyExpl)|Expl]) :-
clause(Goal,Body),
clause_body_list(Body,BodyL),
expl(BodyL,BodyExpl),
expl(Goals,Expl).
% turn output of clause/2 into a list
clause_body_list(true,[]) :- !.
clause_body_list((A,B),[A|BL]) :- !,
clause_body_list(B,BL).
clause_body_list(A,[A]) :- !.
这将产生:
?- expl([route(sea,jfk)],X).
X = [(route(sea, jfk), [(flight(sea, msp), []), (flight(msp, jfk), [])])].
它还支持回溯和带有变量的查询:
?- expl([route(A,B)],X).
A = sea,
B = msp,
X = [(route(sea, msp), [(flight(sea, msp), [])])] ;
A = msp,
B = jfk,
X = [(route(msp, jfk), [(flight(msp, jfk), [])])] ;
A = msp,
B = sea,
X = [(route(msp, sea), [(flight(sea, msp), [])])] ;
A = jfk,
B = msp,
X = [(route(jfk, msp), [(flight(msp, jfk), [])])] ;
A = sea,
B = jfk,
X = [(route(sea, jfk), [(flight(sea, msp), []), (flight(msp, jfk), [])])] ;
false.
请注意,这样的解释不一定是列表,而是通常采用(SLD)树的形式,因此是输出的嵌套结构。
编辑:为了进一步解释上述内容:输出是格式为(Goal, BodyExpl)
的“解释”列表,其中每个Goal
是一个(sub)已证明的目标,BodyExpl
再次是用于证明Goal
的所有递归子目标的此类解释的列表。实际上,BodyExpl
部分只是空的。通常,此结构可以任意深度嵌套(取决于您的输入程序)。
如果您觉得这很难读,对进一步处理输出不感兴趣,并且只想要一个易于理解的解释,则可以执行以下操作:
flight(sea,msp).
flight(msp,jfk).
route(A,B) :- flight(A,B).
route(B,A) :- flight(A,B).
route(A,C) :- flight(A,B) , flight(B,C).
% construct an explanation for a solution
expl([]).
expl([Goal|Goals]) :-
clause(Goal,Body),
clause_body_list(Body,BodyL),
expl(BodyL),
expl(Goals),
write_expl(Goal,Body).
% turn output of clause/2 into a list
clause_body_list(true,[]) :- !.
clause_body_list((A,B),[A|BL]) :- !,
clause_body_list(B,BL).
clause_body_list(A,[A]) :- !.
% write explanation
write_expl(Goal, true) :- !,
writef('%w is a fact.\n',[Goal]).
write_expl(Goal, Body) :- !,
writef('%w because of %w.\n', [Goal,Body]).
这将产生例如:
?- expl([route(sea,jfk)]).
flight(msp,jfk) is a fact.
flight(sea,msp) is a fact.
route(sea,jfk) because of flight(sea,msp),flight(msp,jfk).
请注意,您只想在对write_expl
进行递归调用之后 调用expl
,因为某些变量可能仅在递归调用期间被实例化。>