简体   繁体   English

堆栈跟踪Prolog谓词

[英]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: 1case 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]) ,通过将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 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 . 从这里开始,它返回到子句列表的顶部(在新查询中,它从顶部开始),并且能够通过将F1统一来统一第一个子句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统一为2F统一为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])统一,并且可以通过将E1L[]统一来实现。 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])统一(注:通过将第一个F1统一,将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统一(但是未例示),将F2以及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 =< F1 =< 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)所述失败),原始查询通过将E1统一而成功完成,我们看到:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM