Prolog - 返回结果而不是在算法中打印

时间:2016-03-25 16:08:57

标签: algorithm prolog prolog-toplevel logical-purity

我知道Prolog在技术上没有“回归”,但我不知道如何制定这个问题。

我找到了一些用于在地铁站之间寻找路线的算法示例代码。它运作良好,但它应该只打印结果,因此很难扩展或例如findall/3

% direct routes
findRoute(X,Y,Lines,Output) :- 
    line(Line,Stations),
    \+ member(Line,Lines),
    member(X,Stations),
    member(Y,Stations),
    append(Output,[[X,Line,Y]],NewOutput),
    print(NewOutput).

% needs intermediate stop
findRoute(X,Y,Lines,Output) :- 
    line(Line,Stations),
    \+ member(Line,Lines),
    member(X,Stations),
    member(Intermediate,Stations),
    X\=Intermediate,Intermediate\=Y,
    append(Output,[[X,Line,Intermediate]],NewOutput),
    findRoute(Intermediate,Y,[Line|Lines],NewOutput).

line是一个带有原子的谓词和包含这些站的列表 例如:line(s1, [first_stop, second_stop, third_stop])

所以我要做的就是在第11行摆脱print并在我的规则中添加一个额外的变量来存储结果供以后使用。但是我失败了,因为无论我尝试什么,它都会进入无限循环或返回false。

现在:

?- findRoute(first_stop, third_stop, [], []).
% prints [[first_stop,s1,third_stop]]

想要:

?- findRoute(first_stop, third_stop, [], R).
% [[first_stop,s1,third_stop]] is stored in R

1 个答案:

答案 0 :(得分:3)

和你一样,我也经常在Prolog初学者中看到这种模式,特别是如果他们使用坏书和其他材料:

solve :-
     .... some goals ...
     compute(A),
     write(A).

由于以下原因,上述几乎每一行都存在问题:

  1. "解决"是势在必行。这在像Prolog这样的声明性语言中没有意义,因为你可以在几个方向上使用谓词。
  2. "计算"也很势在必行。
  3. write/1是副作用,其输出仅在系统终端上可用。这使我们没有简单的方法来实际测试谓词。
  4. 这些模式应该总是看起来像:

    solution(S) :-
         condition1(...),
         condition2(...),
         condition_n(S).
    

    其中condition1等只是纯粹的目标,描述了S是解决方案的意义。

    查询时

    ?- solution(S).
    

    然后S的绑定将自动打印在顶层。让toplevel为你做印刷!

    在您的情况下,有一个直接的解决方法:简单地使NewOutput其中一个参数,并删除最终的副作用:

    route(X, Y, Lines, Output, NewOutput) :- 
        line(Line, Stations),
        \+ member(Line, Lines),
        member(X, Stations),
        member(Y, Stations),
        append(Output, [[X,Line,Y]], NewOutput).
    

    另请注意,我已将名称更改为route/5,因为如果参数已全部已实例化,则谓词也是,这对于测试等非常有用。

    此外,在描述列表时,使用表示法通常会从中受益匪浅。

    代码看起来与此类似:

    route(S, S, _) --> [].          % case 1: already there
    route(S0, S, Lines) -->         % case 2: needs intermediate stop
        { line_stations(Line, Stations0),
          maplist(dif(Line), Lines),
          select(S0, Stations0, Stations),
          member(S1, Stations) },
        [link(S0,Line,S1)],
        route(S1, S, [Line|Lines]).
    

    方便的是,您可以使用它来描述列表的串联,而不需要append/3这么多。我还做了一些其他的改进,以提高纯度和可读性,我将确切的差异作为一个简单的练习。

    您可以使用DCG接口谓词phrase/2来调用它,使用:

    ?- phrase(route(X,Y,[]), Rs).
    

    其中Rs是找到的路线。另请注意,我使用link/3形式的术语来表示路线的链接。当知道arity时,使用专用术语是一种好习惯。如果您事先不知道需要表示多少元素,列表就是好的。