为什么相同的Prolog查询仅在特定情况下产生重复结果?

时间:2016-10-16 11:02:58

标签: prolog

在Prolog工作时,我经常遇到我的实现在查询特定事物时会产生重复的结果。以下是我的知识库:

connection(bus,kerkrade, heerlen, 1100, 1200).
connection(bus,kerkrade, bleijerheide, 1100, 1125).
connection(plane, kerkrade, sittard, 1100, 2100).
connection(bus, bleijerheide, heerlen, 1200, 1215).
connection(bus,heerlen, kerkrade, 1115, 1230).
connection(bus,heerlen, maastricht, 1230, 1330).
connection(plane, heerlen, groningen, 1400, 1420).
connection(bus,maastricht, heerlen, 1430, 1530).
connection(plane,maastricht, sittard, 1415, 1430).
connection(bus,maastricht, sittard, 1345, 1445).
connection(train,maastricht,sittard,1345,1600).
connection(bus,sittard, maastricht, 1630, 1530).
connection(bus,sittard, denbosch, 1530, 1700).
connection(plane,sittard, groningen, 1520 ,1700).
connection(bus,denbosch, sittard, 1800, 1930).
connection(bus,denbosch, amsterdam, 1000, 1330).

validConnection(T,X,Y,Z, connection(B,X,Y,S,W)):-
    connection(B,X,Y,S,W),
    member(B,T),
    (Z =< S).

findRoute(TransportList,DepartureLoc,ArrivalLoc,TimeLeftAtHome,ToSolve):-
    route(TransportList,DepartureLoc,ArrivalLoc,TimeLeftAtHome,[],ToSolve).

route(T,A,B,Time,V,[Head|L]) :-
    validConnection(T,A,X,Time,Head),
    not(member(X,V)),
    (
        B = X, L=[];
        connection(_,A,X,_,S),
        route(T,X,B,S,[A|V],L)
    ).

询问Heerlen和Sittard之间是否有路线,我得到以下非重复结果:

findRoute([bus,train,plane],heerlen,sittard,900,V)

V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(plane,    maastricht, sittard, 1415, 1430)] ;
V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(bus, maastricht, sittard, 1345, 1445)] ;
V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(train, maastricht, sittard, 1345, 1600)] ;
false.

然而,再次查询,但这次我们的目的地是格罗宁根,我收到以下输出,其中每个结果都是重复的:

findRoute([bus,train,plane],heerlen,groningen,900,V)

V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(plane, maastricht, sittard, 1415, 1430), connection(plane, sittard, groningen, 1520, 1700)] ;
V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(plane, maastricht, sittard, 1415, 1430), connection(plane, sittard, groningen, 1520, 1700)] ;
V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(bus, maastricht, sittard, 1345, 1445), connection(plane, sittard, groningen, 1520, 1700)] ;
V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(bus, maastricht, sittard, 1345, 1445), connection(plane, sittard, groningen, 1520, 1700)] ;
V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(train, maastricht, sittard, 1345, 1600), connection(plane, sittard, groningen, 1520, 1700)] ;
V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(train, maastricht, sittard, 1345, 1600), connection(plane, sittard, groningen, 1520, 1700)] ;
V = [connection(plane, heerlen, groningen, 1400, 1420)] ;
false.

使用内置调试器我一直在记录正在发生的事情并且无法查看问题所在。我已尝试对我的KB进行一些小的编辑,但它会产生相同的结果。 我知道我可以使用setof\3和其他方法来否定重复的结果,但这不是我感兴趣的。我还检查了事实,看看它们中是否有任何重复但是没有

第二个查询如何产生重复结果,而第一个查询却没有?

1 个答案:

答案 0 :(得分:1)

您可以尝试在程序中的各个位置剪切(!),但这并不容易。一个简单的方法是写:

findall(V,findRoute([bus,train,plane],heerlen,groningen,900,V),L),sort(L,L1).

这将创建一个包含所有答案的列表(L1)列表。要获得一个答案(一个列表)并按“;”获得另一个答案(当然没有重复),您可以添加:

member(L1,Result).

要回答你的问题,问题在于:

 connection(_,A,X,_,S),
 route(T,X,B,S,[A|V],L)

你想说是否有连接then找到路线... 但这应该使用->(然后是运算符):

 B = X, L=[];
 connection(_,A,X,_,S)->
 route(T,X,B,S,[A|V],L)

示例:

?- findRoute([bus,train,plane],heerlen,groningen,900,V).
V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(plane, maastricht, sittard, 1415, 1430), connection(plane, sittard, groningen, 1520, 1700)] ;
V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(bus, maastricht, sittard, 1345, 1445), connection(plane, sittard, groningen, 1520, 1700)] ;
V = [connection(bus, heerlen, maastricht, 1230, 1330), connection(train, maastricht, sittard, 1345, 1600), connection(plane, sittard, groningen, 1520, 1700)] ;
V = [connection(plane, heerlen, groningen, 1400, 1420)] ;
false.

这正是上面的句子目标应该用Prolog代码编写的。通过使用,(和)它是不一样的,并找到更多的命令。

作为结论,找到这样的副本并不容易,(在更复杂的程序中可能会非常困难),因此使用上述解决方案和findall或其他任何可以删除重复的解决方案可能会更容易。