2017-08-05 52 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.