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

Prolog - Return result instead of printing in algorithm

我知道在 Prolog 中技术上没有 'return' 但我不知道如何提出问题。

我找到了一些用于查找地铁站之间路线的算法示例代码。它运行良好,但它应该只打印结果,因此很难扩展或执行 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 并在我的规则中添加一个额外的变量来存储结果供以后使用。但是我惨败了,因为无论我尝试什么,它要么进入无限循环,要么进入 returns 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

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

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

上面几乎每一行都有问题,原因如下:

  1. "solve" 是 必须的。这在像 Prolog 这样的声明性语言中没有意义,因为您可以在多个方向上使用谓词。
  2. "compute"也是势在必行
  3. write/1 是一个副作用,它的输出只在系统终端上可用。这给我们提供了实际测试谓词的简单方法。

此类模式应始终类似于:

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

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

查询时

?- solution(S).

然后 S 的绑定将自动打印 在顶层。让顶层为您打印!

对于您的情况,有一个直接的解决方法:只需将 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 已知时,使用专用术语是一种很好的做法。例如,如果您事先不知道需要表示多少元素,列表就很好。