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).
上面几乎每一行都有问题,原因如下:
- "solve" 是 必须的。这在像 Prolog 这样的声明性语言中没有意义,因为您可以在多个方向上使用谓词。
- "compute"也是势在必行
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
,因为谓词也有意义如果参数都已实例化,这对测试很有用等等
此外,在描述列表时,使用 dcg 符号通常会使您受益匪浅。
代码将类似于:
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 已知时,使用专用术语是一种很好的做法。例如,如果您事先不知道需要表示多少元素,列表就很好。
我知道在 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).
上面几乎每一行都有问题,原因如下:
- "solve" 是 必须的。这在像 Prolog 这样的声明性语言中没有意义,因为您可以在多个方向上使用谓词。
- "compute"也是势在必行
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
,因为谓词也有意义如果参数都已实例化,这对测试很有用等等
此外,在描述列表时,使用 dcg 符号通常会使您受益匪浅。
代码将类似于:
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 已知时,使用专用术语是一种很好的做法。例如,如果您事先不知道需要表示多少元素,列表就很好。