![](/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.