[英]Stack Trace Prolog Predicate
I have a very simple min function written in Prolog, and I don't understand how it works. 我有一个用Prolog编写的非常简单的min函数,但我不明白它是如何工作的。
Code: 码:
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.
We've just started using Prolog in class, and I don't understand how it evaluates recursive cases like this one. 我们刚刚在课堂上开始使用Prolog,但我不明白它如何评估像这样的递归案例。 I included print statements in this function to see what's going on, and I don't understand some of the steps here:
我在此函数中包含了打印语句,以查看发生了什么,而我不理解此处的某些步骤:
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 .
I understand the first two calls, but I don't understand what happens after the line case 1: 1
. 我了解前两个电话,但不了解第
case 1: 1
行case 1: 1
之后发生的case 1: 1
。 Why does it call on line 3 the second case min(E, [E|L])
after going to case 1, min(E, [E])
? 为什么在第1行
min(E, [E|L])
之后在第3行调用第二种情况min(E, [E|L])
min(E, [E])
? That doesn't follow from anywhere in the code. 这并不是从代码中的任何地方开始的。 If someone could explain what's going on after those first two calls, that would be great.
如果有人可以解释在前两个电话之后发生了什么,那就太好了。 I've looked around for some explanations, but I haven't been able to understand what's going on here.
我到处寻找一些解释,但是我无法理解这里发生的事情。
To figure this out, we'll play prolog interpreter. 为了弄清楚这一点,我们将播放序言解释器。 :)
:)
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.
We do the query: 我们进行查询:
min(E, [2,1]).
(A) Prolog starts from the first clause, min(E, [E])
and fails since [2,1]
can't unify with [E]
. (A)Prolog从第一个子句
min(E, [E])
,但由于[2,1]
无法与[E]
统一而失败。 It then goes to the next clause, min(E, [E|L])
, and is able to unify [2,1]
with [E|L]
by unifying E
with 2
and L
with [1]
and then we see: 然后转到下一个子句
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 then makes the recursive query, min(F, [1])
. (B)然后,Prolog进行递归查询
min(F, [1])
。 From here, it's back to the top of the clause list (on a new query, it starts from the top) and is able to unify the variables in the first clause, min(E, [E])
by unifying F
with 1
. 从这里开始,它返回到子句列表的顶部(在新查询中,它从顶部开始),并且能够通过将
F
与1
统一来统一第一个子句min(E, [E])
的变量。 We then see: 然后,我们看到:
case 1: 1
(C) This query succeeds and comes back to the clause it was queried from and encounters E =< F
where E
is unified with 2
and F
is unified with 1
. (C)此查询成功,并返回到从其查询的子句,并遇到
E =< F
,其中E
统一为2
且F
统一为1
。 But then E =< F
will fail since 1 =< 2
is not true. 但是然后
E =< F
将失败,因为1 =< 2
是不正确的。 At this point, Prolog will backtrack and re-attempt the prior recursive query it just did, min(F, [1])
. 此时,Prolog将回溯并重新尝试它刚刚执行的先前递归查询
min(F, [1])
。 Recall that the query had already exercised the first clause and succeeded so now on backtrack it will attempt the second clause. 回想一下,该查询已经执行了第一个子句并成功执行,因此现在回溯将尝试第二个子句。 It looks to unify
min(F, [1])
with min(E, [E|L])
and can do so by unifying E
with 1
and L
with []
. 它看起来将
min(F, [1])
与min(E, [E|L])
统一,并且可以通过将E
与1
和L
与[]
统一来实现。 Then clause 2 executes and we get: 然后执行子句2,我们得到:
case 2: 1 [1]
(D) We're now an additional call deep in clause 2. We haven't finished the first one yet. (D)我们现在在第2节中进行了另一个致电。我们还没有完成第一个电话。 So this new call will query
min(F, [])
(remember L
is unified with []
in this case). 因此,此新调用将查询
min(F, [])
(在这种情况下,请记住L
与[]
统一)。 There are no clauses in your predicate that match min(F, [])
, so it fails. 谓词中没有与
min(F, [])
匹配的子句,因此它失败。 So this instance of the case 2 query fails completely (backtracks through the writes
which don't re-execute on backtrack). 因此,案例2查询的此实例完全失败(通过回
writes
进行回溯,而不会在回溯上重新执行)。 This was the recursive query from (C) above. 这是上面(C)中的递归查询。
(E) Since case 2 failed on the recursive call from (C), Prolog continues to backtrack and reattempts by executing the third clause and unifies min(E, [F|L])
with min(F, [1])
(note: these are "different" F's) by unifying the first F
with 1
, the L
with []
and E
is unified with the second F
(but is uninstantiated - does not have a value assigned). (E)由于案例2在(C)的递归调用中失败,因此Prolog继续回溯并通过执行第三子句重新尝试,并将
min(E, [F|L])
与min(F, [1])
统一(注:通过将第一个F
与1
统一,将L
与[]
和E
统一为第二个F
(但未实例化-未分配值),将它们“区别开”。 It's important to note here that in Prolog two variables can be unified but not yet be assigned a value. 重要的是,在Prolog中,两个变量可以统一但尚未分配值。 Since the head of the third clause has been unified, case 3 executes and we see:
由于第三个子句的标题已经统一,因此案例3会执行,我们会看到:
case 3: _L164 [1] % This is E (uninstantiated) and [F|L] ([1|[]])
The _L164
appears because we are writing an uninstantiated variable. 出现
_L164
原因是我们正在编写一个未实例化的变量。 Uninstantiated variables, in output like this, appear as a generated variable name preceded by an underscore ( _
). 未实例化的变量在这样的输出中显示为生成的变量名,并带有下划线(
_
)。
(F) So case 3 executes and does a recursive call to min(E, L)
where E
is uninstantiated and L
is []
. (F)因此,情况3执行并递归调用
min(E, L)
,其中E
未实例化, L
为[]
。 This query will fail because there are no clauses that match min(_, [])
. 该查询将失败,因为没有匹配
min(_, [])
子句。 Prolog will then backtrack from case 3 and then the entire recursive call from (C) to min(F, [1])
has failed. 然后,Prolog将从情况3回溯,然后从(C)到
min(F, [1])
的整个递归调用失败。
(G) Remember that we got to (F) from a recursive call in case 2 described in (C). (G)请记住,在(C)中描述的情况2中,我们是通过递归调用到达(F)的。 Since that recursive call has failed (as described in (D) through (F)) Prolog resumes the case 2 described in (C) by backtracking, failing case 2, and moving on to case 3. This whole execution of the predicate was from the original query,
min(E, [2,1])
. 由于该递归调用失败(如(D)到(F)中所述),Prolog通过回溯,使案例2失败并继续进行到案例3来恢复(C)中描述的案例2。整个谓词的执行是从原始查询
min(E, [2,1])
。 The head of the third clause is min(E, [F|L])
and Prolog unifies the first E
with the second E
(uninstantiated, however), unifies F
with 2
and L
with [1]
. 第三子句的开头是
min(E, [F|L])
,Prolog将第一个E
与第二个E
统一(但是未例示),将F
与2
以及L
与[1]
统一。 We now see: 现在我们看到:
case 3: _G323 [2,1] % This is E (uninstantiated) and [F|L] ([2|[1]])
(H) Case 3 proceeds and does a recursive query on min(E, [1])
(having instantiated L
with [1]
) which starts from the top again, matches the first clause min(E, [E])
and prolog unifies E
with 1
and matches the head of the clause.. We then see: (H)情况3继续进行,并对
min(E, [1])
(具有用[1]
实例化的L
min(E, [1])
进行递归查询,该查询再次从顶部开始,与第一个子句min(E, [E])
和prolog匹配用1
统一E
并匹配子句的开头。然后,我们看到:
case 1: 1
(I) Case 1 succeeds, and comes back to case 3 which proceeds and checks E =< F
which is 1 =< 2
(see unifications in (G)) which is true. (I)情况1成功,然后返回情况3,继续进行并检查
E =< F
为1 =< 2
(请参阅(G)中的统一)是否为真。 We've now completely succeeded case 3! 现在,我们已经完全成功案例3!
And we're done! 我们完成了! With the success of case 3 (case 1 having failed as described in (A), and case 2 having failed as described in (E)), the original query has succeeded by unifying
E
with 1
and we see: 随着案例3的成功(案例1如(A)所述失败,案例2如(E)所述失败),原始查询通过将
E
与1
统一而成功完成,我们看到:
E = 1.
When you do a query in Prolog, it will start with the first clause of the predicate you are querying and attempt each clause, in sequence, until it finds one that is successful and then it will declare success. 当您在Prolog中进行查询时,它将以您要查询的谓词的第一个子句开始,并按顺序尝试每个子句,直到找到成功的子句,然后声明成功。 If they all fail, then, of course, the query fails.
如果它们全部失败,则查询当然会失败。 In the process of attempting each clause, if there is a recursive query (call), that recursive call will start from the first clause again.
在尝试每个子句的过程中,如果存在一个递归查询(调用),则该递归调用将再次从第一个子句开始。 Each recursive call is its own full-blown query on the predicate.
每个递归调用都是其对谓词的完整查询。 Thus, each recursive call will start back at the first clause of the predicate, taking it's own journey of truth-seeking through each of the predicate's clauses.
因此,每个递归调用都将从谓词的第一个子句开始,这是它自己通过每个谓词的子句寻求真相的过程。 This is an important principle to know about Prolog which will help understand basic recursive behavior.
这是了解Prolog的重要原则,这将有助于理解基本的递归行为。
On the topic of tracing, the write
statements in the code do a good job of showing what clauses of the predicate fire. 关于跟踪的主题,代码中的
write
语句很好地显示了谓词fire的哪些子句。 But they don't show what queries within the clauses fail, which is just as important to know when trying to understand what's happening in the queries. 但是它们没有显示子句中的哪些查询失败,因此在试图了解查询中发生的情况时知道这一点同样重要。 So it can be a little confusing with just the
write
statements. 因此,仅
write
语句可能会使您感到困惑。 The gtrace
(or trace
) command, suggested by @User, would show succeeding and failing queries. gtrace
建议的gtrace
(或trace
)命令将显示成功和失败的查询。 It's a good tool to use to see what's happening in the clauses, perhaps in conjunction with the write
statements to peek at variables and such. 这是一个很好的工具,可以用来查看子句中发生的事情,也许可以与
write
语句一起查看变量等。
You can use gtrace
in SWI-Prolog to trace evaluation. 您可以在SWI-Prolog中使用
gtrace
来跟踪评估。
10 ?- gtrace, min(E, [2, 1]). 10?-gtrace,min(E,[2,1])。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.