![](/img/trans.png)
[英]Need help understanding Prolog append/3 and inverse/2 and trace output
[英]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: 1
行case 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])
,通過將E
與2
和L
與[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])
。 從這里開始,它返回到子句列表的頂部(在新查詢中,它從頂部開始),並且能夠通過將F
與1
統一來統一第一個子句min(E, [E])
的變量。 然后,我們看到:
case 1: 1
(C)此查詢成功,並返回到從其查詢的子句,並遇到E =< F
,其中E
統一為2
且F
統一為1
。 但是然后E =< F
將失敗,因為1 =< 2
是不正確的。 此時,Prolog將回溯並重新嘗試它剛剛執行的先前遞歸查詢min(F, [1])
。 回想一下,該查詢已經執行了第一個子句並成功執行,因此現在回溯將嘗試第二個子句。 它看起來將min(F, [1])
與min(E, [E|L])
統一,並且可以通過將E
與1
和L
與[]
統一來實現。 然后執行子句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])
統一(注:通過將第一個F
與1
統一,將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
統一(但是未例示),將F
與2
以及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 =< F
為1 =< 2
(請參閱(G)中的統一)是否為真。 現在,我們已經完全成功案例3!
我們完成了! 隨着案例3的成功(案例1如(A)所述失敗,案例2如(E)所述失敗),原始查詢通過將E
與1
統一而成功完成,我們看到:
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.