2017-08-05 51 views
1

我试图创建一个谓词,每次输出所有列表减一个元素。对于列表[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). 

这似乎是工作,但是当试图执行它我” m得到以下错误:

?- 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 

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

+0

这意味着你进入无限递归。 –

+0

@WillemVanOnsem,但我没有使用递归 – randomname123

+0

这是没有必要的,'append/3'递归构建。 –

回答

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). 

代码的工作原理如下:如果第一个参数与[](空单)相结合,我们返回空列表,并留下部分被排除在未磨过的地方。如果第一个参数与[H|T](带有头部H和尾部T的列表)一致,则有两种选择:我们选择头部H作为要排除的元素,并因此返回尾部T,或者我们推迟排除,并让递归调用选择一个元素。

的上述计划将使我们能够排除没有元素:

?- 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.