如何修复这个反向函数,以便它不会堆栈溢出?

时间:2017-11-12 18:15:15

标签: prolog

我从http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse25得到了这个反向函数:

accRev([H|T],A,R):-  accRev(T,[H|A],R). 
accRev([],A,A).

rev(L,R):-  accRev(L,[],R).

这适用于

之类的内容
?- rev([a,b,c], R).
R = [c,b,a]

但是我得到了一个堆栈溢出:

?- rev(L, [c,b,a]).

为什么会导致堆栈溢出?有没有办法使两者都有效?:

1 个答案:

答案 0 :(得分:4)

  

?- rev(L, [c,b,a]).

     

为什么会导致堆栈溢出?

Sssh ......你正在加速Prolog引擎。

实际上有两个原因:第一,查询没有终止。然后,它以相当无效的方式搜索解决方案。

让我"解决"通过首先解决第二个原因来解决这个问题。只需交换accRev/3的两个条款。这改变了答案:

?- rev(L, [c,b,a]).
ERROR: Out of local stack

?- rev(L, [c,b,a]).
L = [a, b, c]
很好 - 或者差不多。请注意,Prolog没有在解决方案的末尾添加.。这意味着它说:想要更多这个?因此,输入;即可获得:

?- rev(L, [c,b,a]).
L = [a, b, c] ;
ERROR: Out of global stack

所以我们几乎"解决了这个问题。我们找到了解决方案,但Prolog仍未终止。这很好地说明了纯Prolog程序的一个属性:

  

交换条款可能会影响解决方案/答案的找到方式,但不会影响终止。

确保目标实际终止的最佳方法是关闭"关闭"我们通过添加false获得的所有答案:

?- rev(L, [c,b,a]), false.
ERROR: Out of global stack

无论子句如何排列,此错误现在都以相同的方式发生。事实上,原因可以缩小到以下

rev(L,R):-
   accRev(L,[],R), false.

accRev([],A,A) :- false.
accRev([H|T],A,R):-
   accRev(T,[H|A],R), false.

注意R,它只是进一步传递。因此它对终止没有任何影响。

但请注意,LR的长度相同......

所以也许首先考虑same_length/2,它可以直接解决这个问题。

same_length([], []).
same_length([_|L], [_|R]) :-
   same_length(L, R).

rev_better(L, R) :-
   same_length(L, R),
   rev(L, R).

或者,如果您进入"效率",只需观察same_length/2可以直接折叠:

rev_folded(L, R) :-
   accrev(L, [],R, R).

accrev([], R,R, []).
accrev([E|L], R0,R, [_|Rx]) :-
   accrev(L, [E|R0],R, Rx).