如何让Prolog在真实陈述之外解释你的结果

时间:2015-06-04 16:54:25

标签: prolog

我有以下事实和规则:

  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这样我不仅可以说明它是真的,还可以告诉它是如何真实的。

4 个答案:

答案 0 :(得分:3)

所以你想从AB,但不仅如此,你还想知道行程的电台列表。

请务必仔细查看以下两个相关问题以及针对该问题提出的答案:

上面链接中提供的元谓词允许您将递归处理委托给可靠,经过测试,可重用的组件。有更多时间专注于解决问题的其他部分!

答案 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。最有趣的可能是下一步nf完成当前目标。

或者也可以使用trace/1trace/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,因为某些变量可能仅在递归调用期间被实例化。