错误:超出全局堆栈

时间:2017-08-05 19:35:38

标签: prolog

我正在尝试创建一个谓词,每次输出所有列表减去一个元素。对于列表[a,b,c],我想要谓词返回

a, [b, c]
b, [a, c]
c, [a, b]

我创建了跟随谓词:

elem_list(Elems, X, ElemsNoX) :-
    append(ElemsA, [X], ElemsAX),
    append(ElemsAX, ElemsB, Elems),
    append(ElemsA, ElemsB, ElemsNoX).

这似乎有效,但在尝试执行时,我收到以下错误:

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

这个错误是什么意思?我该如何解决?

1 个答案:

答案 0 :(得分:1)

在大多数Prolog版本中,您可以使用trace.启用一个模式,其中打印Prolog解释器所采用的每个步骤。为谓词打开trace.时,会显示:

[trace]  ?- elem_list([a,b,c], X, L).
   Call: (7) elem_list([a, b, c], _G25166352, _G25166353) ? creep
   Call: (8) lists:append(_G25166456, [_G25166352], _G25166458) ? creep
   Exit: (8) lists:append([], [_G25166352], [_G25166352]) ? creep
   Call: (8) lists:append([_G25166352], _G25166457, [a, b, c]) ? creep
   Exit: (8) lists:append([a], [b, c], [a, b, c]) ? creep
   Call: (8) lists:append([], [b, c], _G25166353) ? creep
   Exit: (8) lists:append([], [b, c], [b, c]) ? creep
   Exit: (7) elem_list([a, b, c], a, [b, c]) ? creep
X = a,
L = [b, c] ;
   Redo: (8) lists:append(_G25166456, [_G25166352], _G25166458) ? creep
   Exit: (8) lists:append([_G25166449], [_G25166352], [_G25166449, _G25166352]) ? creep
   Call: (8) lists:append([_G25166449, _G25166352], _G25166463, [a, b, c]) ? creep
   Exit: (8) lists:append([a, b], [c], [a, b, c]) ? creep
   Call: (8) lists:append([a], [c], _G25166353) ? creep
   Exit: (8) lists:append([a], [c], [a, c]) ? creep
   Exit: (7) elem_list([a, b, c], b, [a, c]) ? creep
X = b,
L = [a, c] ;
   Redo: (8) lists:append([_G25166449|_G25166450], [_G25166352], [_G25166449|_G25166453]) ? creep
   Exit: (8) lists:append([_G25166449, _G25166455], [_G25166352], [_G25166449, _G25166455, _G25166352]) ? creep
   Call: (8) lists:append([_G25166449, _G25166455, _G25166352], _G25166469, [a, b, c]) ? creep
   Exit: (8) lists:append([a, b, c], [], [a, b, c]) ? creep
   Call: (8) lists:append([a, b], [], _G25166353) ? creep
   Exit: (8) lists:append([a, b], [], [a, b]) ? creep
   Exit: (7) elem_list([a, b, c], c, [a, b]) ? creep
X = c,
L = [a, b] ;
   Redo: (8) lists:append([_G25166449, _G25166455|_G25166456], [_G25166352], [_G25166449, _G25166455|_G25166459]) ? creep
   Exit: (8) lists:append([_G25166449, _G25166455, _G25166461], [_G25166352], [_G25166449, _G25166455, _G25166461, _G25166352]) ? creep
   Call: (8) lists:append([_G25166449, _G25166455, _G25166461, _G25166352], _G25166475, [a, b, c]) ? creep
   Fail: (8) lists:append([_G25166449, _G25166455, _G25166461, _G25166352], _G25166475, [a, b, c]) ? creep

所以我们看到第一个 append/3调用(注意[_G25166352]作为第二个参数)。只需生成新变量。我们可以通过执行隔离呼叫来确认这一点:

?- append(ElemsA, [X], ElemsAX).
ElemsA = [],
ElemsAX = [X] ;
ElemsA = [_G25167627],
ElemsAX = [_G25167627, X] ;
ElemsA = [_G25167627, _G25167633],
ElemsAX = [_G25167627, _G25167633, X] ;
ElemsA = [_G25167627, _G25167633, _G25167639],
ElemsAX = [_G25167627, _G25167633, _G25167639, X]

所以会发生的事情就是附加一个左侧和右侧都有未接地变量的列表。当然,这些列表没有机会完全接受:这些列表包含的元素多于初始列表[a,b,c],因此这意味着这些元素最终会在后续过程中失败(我们可以看到,当我们进一步跟踪程序时)。但显然第一个谓词并不知道,因此只是继续构建列表,直到内存耗尽为止。

上述解决方案因此不是一个好方法。

我们可以编写一个成功的自定义谓词:

exclude([],_,[]).
exclude([H|T],H,T).
exclude([H|T],X,[H|T2]) :-
    exclude(T,X,T2).

代码的工作方式如下:如果第一个参数与[](空列表)统一,我们返回空列表并将部分保留为unground。如果第一个参数与[H|T](带有头H和尾T的列表)统一,则有两个选项:我们选择头H作为元素排除,从而返回尾T,或者我们推迟排除,并让递归调用选择一个元素。

上述程序将允许我们排除 no 元素:

?- exclude([a,b,c], X, L).
X = a,
L = [b, c] ;
X = b,
L = [a, c] ;
X = c,
L = [a, b] ;
L = [a, b, c].

因此,最后一行只会使X无效,并返回整个列表[a,b,c]

如果您不想这样,您只需删除第一行:

exclude1([H|T],H,T).
exclude1([H|T],X,[H|T2]) :-
    exclude1(T,X,T2).

现在Prolog被迫将第一个列表中的至少一个元素作为要排除的元素。这将产生:

?- exclude1([a,b,c], X, L).
X = a,
L = [b, c] ;
X = b,
L = [a, c] ;
X = c,
L = [a, b] ;
false.