【问题标题】:How does recursive back tracking works in Prolog递归回溯如何在 Prolog 中工作
【发布时间】:2019-11-07 18:14:17
【问题描述】:

我正在学习 Prolog,但我很难理解递归回溯。我试图了解回溯时搜索树的样子,但我有点迷路了。

例如下面的代码行,从列表中删除所有出现的元素:

remove_all(_, [], []).
remove_all(El, [El|Tail], Tail2):- 
    remove_all(El, Tail, Tail2).
remove_all(El, [Head|Tail], [Head|Tail2]):-
    not(El = Head),
    remove_all(El, Tail, Tail2).

现在我查询以下内容?-remove_all( 2, [1,4,2,3,5,2,7,2], X).我使用在线 SWISH Prolog 来执行它,并得到以下树

 Call:remove_all(2, [1, 4, 2, 3, 5, 2, 7, 2], _4896)
 Call:not(2=1)
 Exit:not('10758ac0-dc91-4335-a0d8-48b7b11776c0' : (2=1))
 Call:remove_all(2, [4, 2, 3, 5, 2, 7, 2], _5128)
 Call:not(2=4)
 Exit:not('10758ac0-dc91-4335-a0d8-48b7b11776c0' : (2=4))
 Call:remove_all(2, [2, 3, 5, 2, 7, 2], _5146)
 Call:remove_all(2, [3, 5, 2, 7, 2], _5146)
 Call:not(2=3)
 Exit:not('10758ac0-dc91-4335-a0d8-48b7b11776c0' : (2=3))
 Call:remove_all(2, [5, 2, 7, 2], _5164)
 Call:not(2=5)
 Exit:not('10758ac0-dc91-4335-a0d8-48b7b11776c0' : (2=5))
 Call:remove_all(2, [2, 7, 2], _5182)
 Call:remove_all(2, [7, 2], _5182)
 Call:not(2=7)
 Exit:not('10758ac0-dc91-4335-a0d8-48b7b11776c0' : (2=7))
 Call:remove_all(2, [2], _5200)
 Call:remove_all(2, [], _5200)
 Exit:remove_all(2, [], [])
 Exit:remove_all(2, [2], [])
 Exit:remove_all(2, [7, 2], [7])
 Exit:remove_all(2, [2, 7, 2], [7])
 Exit:remove_all(2, [5, 2, 7, 2], [5, 7])
 Exit:remove_all(2, [3, 5, 2, 7, 2], [3, 5, 7])
 Exit:remove_all(2, [2, 3, 5, 2, 7, 2], [3, 5, 7])
 Exit:remove_all(2, [4, 2, 3, 5, 2, 7, 2], [4, 3, 5, 7])
 Exit:remove_all(2, [1, 4, 2, 3, 5, 2, 7, 2], [1, 4, 3, 5, 7])
X = [1, 4, 3, 5, 7]

我无法理解的是这些代码行中的回溯是如何发生的

 Exit:remove_all(2, [], [])
 Exit:remove_all(2, [2], [])
 Exit:remove_all(2, [7, 2], [7])
 Exit:remove_all(2, [2, 7, 2], [7])
 Exit:remove_all(2, [5, 2, 7, 2], [5, 7])
 Exit:remove_all(2, [3, 5, 2, 7, 2], [3, 5, 7])
 Exit:remove_all(2, [2, 3, 5, 2, 7, 2], [3, 5, 7])
 Exit:remove_all(2, [4, 2, 3, 5, 2, 7, 2], [4, 3, 5, 7])
 Exit:remove_all(2, [1, 4, 2, 3, 5, 2, 7, 2], [1, 4, 3, 5, 7])

Prolog 解释器如何对此执行递归?能不能解释一下。

【问题讨论】:

    标签: prolog


    【解决方案1】:

    回溯问题与本例无关。

    Prolog 与大多数其他语言一样, 维护一个函数栈。 当一个函数被调用时,它和它的参数被压入堆栈。 当一个函数完成后,它和它的参数将从堆栈中弹出。

    当函数“a”被调用,而“a”又调用函数“b”时:在“b”内,“b”和“a”都开启堆栈;当 'b' 完成时,只有 'a' 在堆栈上;当 'a' 完成时,堆栈上没有任何内容。

    当函数'a'被调用,'a'又调用函数'b','b'又调用函数'c':同时在'a'内仅' a' 在堆栈中:而在 'b' 内,'b' 和 'a' 都在堆栈上:而在 'c' 内,所有的 'c' 和 '@987654342 @' 和 'a' 在堆栈上;当 'c' 完成时,'b 和 'a' 都在堆栈上;当 'b' 完成时,只有 'a' 在堆栈上;当 'a' 完成时,堆栈上没有任何内容。

    如果 'a' 而不是调用 'b' 递归调用自身,也会发生同样的事情。

    当函数'a(1)'被调用,'a(1)'又调用函数'a(2)','a(2)'又调用函数'a(3)':在'a(1)'内只' a(1)' 在堆栈上:而在 'a(2)' 内,'a(2)' 和 'a(1)' 都在堆栈上:而在 'a(3)' 内,所有的 'a(3)' 和 '@987654364 @' 和 'a(1)' 在堆栈上;当 'a(3)' 完成时,'a(2) 和 'a(1)' 都在堆栈上;当 'a(3)' 完成时,只有 'a(1)' 在堆栈上;当 'a(1)`' 完成时,堆栈上没有任何内容。

    示例中的函数递归调用自身,因此它建立了一个堆栈。每次它调用自己时,堆栈深度都会增加。因为每次它调用自己时,列表中会少一个项目,并且当列表中有 0 个项目时它会停止调用自己,因此该函数将建立一个函数调用的堆栈深度,其大小等于原始列表的大小。当函数最终以大小为 0 的列表调用时,它不再递归调用自身;因此它返回,因此堆栈减少 1 ,现在具有大小为 1 的列表的函数调用返回,因此堆栈减少 1 ,现在具有大小为 2 的列表的函数调用返回,因此堆栈减少 1 ,现在是函数调用具有大小为 3 的列表返回,因此堆栈减少 1 ,...等等。

    有了这个解释,你的调试输出的以下格式可能会有所帮助。

            Call:remove_all(2, [1, 4, 2, 3, 5, 2, 7, 2], _4896)
                Call:remove_all(2, [4, 2, 3, 5, 2, 7, 2], _5128)
                    Call:remove_all(2, [2, 3, 5, 2, 7, 2], _5146)
                        Call:remove_all(2, [3, 5, 2, 7, 2], _5146)
                            Call:remove_all(2, [5, 2, 7, 2], _5164)
                                Call:remove_all(2, [2, 7, 2], _5182)
                                    Call:remove_all(2, [7, 2], _5182)
                                        Call:remove_all(2, [2], _5200)
                                            Call:remove_all(2, [], _5200)
                                            Exit:remove_all(2, [], [])
                                        Exit:remove_all(2, [2], [])
                                    Exit:remove_all(2, [7, 2], [7])
                                Exit:remove_all(2, [2, 7, 2], [7])
                            Exit:remove_all(2, [5, 2, 7, 2], [5, 7])
                        Exit:remove_all(2, [3, 5, 2, 7, 2], [3, 5, 7])
                    Exit:remove_all(2, [2, 3, 5, 2, 7, 2], [3, 5, 7])
                Exit:remove_all(2, [4, 2, 3, 5, 2, 7, 2], [4, 3, 5, 7])
            Exit:remove_all(2, [1, 4, 2, 3, 5, 2, 7, 2], [1, 4, 3, 5, 7])
    

    第 56 次

    【讨论】:

      猜你喜欢
      • 2021-12-28
      • 2021-11-16
      • 1970-01-01
      • 2019-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-07
      • 2015-10-26
      相关资源
      最近更新 更多