簡體   English   中英

堆棧跟蹤Prolog謂詞

[英]Stack Trace Prolog Predicate

我有一個用Prolog編寫的非常簡單的min函數,但我不明白它是如何工作的。

碼:

min(E, [E]) :- write('case 1: '), write(E), nl.
min(E, [E|L]) :- write('case 2: '), write(E), write(' '), write([E|L]), nl, min(F, L), E =< F.
min(E, [F|L]) :- write('case 3: '), write(E), write(' '), write([F|L]), nl, min(E, L), E =< F.

我們剛剛在課堂上開始使用Prolog,但我不明白它如何評估像這樣的遞歸案例。 我在此函數中包含了打印語句,以查看發生了什么,而我不理解此處的某些步驟:

10 ?- min(E, [2, 1]).
case 2: 2 [2,1]
case 1: 1
case 2: 1 [1]
case 3: _L164 [1]
case 3: _G323 [2,1]
case 1: 1
E = 1 .

我了解前兩個電話,但不了解第case 1: 1case 1: 1之后發生的case 1: 1 為什么在第1行min(E, [E|L])之后在第3行調用第二種情況min(E, [E|L]) min(E, [E]) 這並不是從代碼中的任何地方開始的。 如果有人可以解釋在前兩個電話之后發生了什么,那就太好了。 我到處尋找一些解釋,但是我無法理解這里發生的事情。

為了弄清楚這一點,我們將播放序言解釋器。 :)

min(E, [E]) :-
    write('case 1: '), write(E), nl.
min(E, [E|L]) :-
    write('case 2: '), write(E), write(' '), write([E|L]), nl,
    min(F, L),
    E =< F.
min(E, [F|L]) :-
    write('case 3: '), write(E), write(' '), write([F|L]), nl,
    min(E, L),
    E =< F.

我們進行查詢:

min(E, [2,1]).

(A)Prolog從第一個子句min(E, [E]) ,但由於[2,1]無法與[E]統一而失敗。 然后轉到下一個子句min(E, [E|L]) ,通過將E2L[1]統一,可以將[2,1][E|L]統一,然后我們看到:

case 2: 2 [2,1]    % This is E instantiated as 2, and [E|L] as [2|[1]]

(B)然后,Prolog進行遞歸查詢min(F, [1]) 從這里開始,它返回到子句列表的頂部(在新查詢中,它從頂部開始),並且能夠通過將F1統一來統一第一個子句min(E, [E])的變量。 然后,我們看到:

case 1: 1

(C)此查詢成功,並返回到從其查詢的子句,並遇到E =< F ,其中E統一為2F統一為1 但是然后E =< F將失敗,因為1 =< 2是不正確的。 此時,Prolog將回溯並重新嘗試它剛剛執行的先前遞歸查詢min(F, [1]) 回想一下,該查詢已經執行了第一個子句並成功執行,因此現在回溯將嘗試第二個子句。 它看起來將min(F, [1])min(E, [E|L])統一,並且可以通過將E1L[]統一來實現。 然后執行子句2,我們得到:

case 2: 1 [1]

(D)我們現在在第2節中進行了另一個致電。我們還沒有完成第一個電話。 因此,此新調用將查詢min(F, []) (在這種情況下,請記住L[]統一)。 謂詞中沒有與min(F, [])匹配的子句,因此它失敗。 因此,案例2查詢的此實例完全失敗(通過回writes進行回溯,而不會在回溯上重新執行)。 這是上面(C)中的遞歸查詢。

(E)由於案例2在(C)的遞歸調用中失敗,因此Prolog繼續回溯並通過執行第三子句重新嘗試,並將min(E, [F|L])min(F, [1])統一(注:通過將第一個F1統一,將L[]E統一為第二個F (但未實例化-未分配值),將它們“區別開”。 重要的是,在Prolog中,兩個變量可以統一但尚未分配值。 由於第三個子句的標題已經統一,因此案例3會執行,我們會看到:

case 3: _L164 [1]    % This is E (uninstantiated) and [F|L] ([1|[]])

出現_L164原因是我們正在編寫一個未實例化的變量。 未實例化的變量在這樣的輸出中顯示為生成的變量名,並帶有下划線( _ )。

(F)因此,情況3執行並遞歸調用min(E, L) ,其中E未實例化, L[] 該查詢將失敗,因為沒有匹配min(_, [])子句。 然后,Prolog將從情況3回溯,然后從(C)到min(F, [1])的整個遞歸調用失敗。

(G)請記住,在(C)中描述的情況2中,我們是通過遞歸調用到達(F)的。 由於該遞歸調用失敗(如(D)到(F)中所述),Prolog通過回溯,使案例2失敗並繼續進行到案例3來恢復(C)中描述的案例2。整個謂詞的執行是從原始查詢min(E, [2,1]) 第三子句的開頭是min(E, [F|L]) ,Prolog將第一個E與第二個E統一(但是未例示),將F2以及L[1]統一。 現在我們看到:

case 3: _G323 [2,1]   % This is E (uninstantiated) and [F|L] ([2|[1]])

(H)情況3繼續進行,並對min(E, [1]) (具有用[1]實例化的L min(E, [1])進行遞歸查詢,該查詢再次從頂部開始,與第一個子句min(E, [E])和prolog匹配用1統一E並匹配子句的開頭。然后,我們看到:

case 1: 1

(I)情況1成功,然后返回情況3,繼續進行並檢查E =< F1 =< 2 (請參閱(G)中的統一)是否為真。 現在,我們已經完全成功案例3!

我們完成了! 隨着案例3的成功(案例1如(A)所述失敗,案例2如(E)所述失敗),原始查詢通過將E1統一而成功完成,我們看到:

E = 1.

當您在Prolog中進行查詢時,它將以您要查詢的謂詞的第一個子句開始,並按順序嘗試每個子句,直到找到成功的子句,然后聲明成功。 如果它們全部失敗,則查詢當然會失敗。 在嘗試每個子句的過程中,如果存在一個遞歸查詢(調用),則該遞歸調用將再次從第一個子句開始。 每個遞歸調用都是其對謂詞的完整查詢。 因此,每個遞歸調用都將從謂詞的第一個子句開始,這是它自己通過每個謂詞的子句尋求真相的過程。 這是了解Prolog的重要原則,這將有助於理解基本的遞歸行為。

關於跟蹤的主題,代碼中的write語句很好地顯示了謂詞fire的哪些子句。 但是它們沒有顯示子句中的哪些查詢失敗,因此在試圖了解查詢中發生的情況時知道這一點同樣重要。 因此,僅write語句可能會使您感到困惑。 gtrace建議的gtrace (或trace )命令將顯示成功和失敗的查詢。 這是一個很好的工具,可以用來查看子句中發生的事情,也許可以與write語句一起查看變量等。

您可以在SWI-Prolog中使用gtrace來跟蹤評估。

10?-gtrace,min(E,[2,1])。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM