Prolog - 这背后的逻辑是什么

时间:2011-05-26 22:13:34

标签: prolog

我试着理解这段代码是如何工作的但是我失败了。 它连接两个列表然后它反转结果。

reverse(L, RL):- reverse(L, [], RL).
reverse([], RL, RL).
reverse([H|T], S, RL):- reverse(T, [H|S], RL).


concat([], L, L).
concat([H|T1], L2, [H|T]):- concat(T1, L2, T).


concat_reverse(L1,L2,L):-concat(L1,L2,LN),reverse(LN,L)

我的老师告诉我进入调试模式并跟踪查询以了解但它没有帮助。

这是一个例子

5 ?- trace,concat_reverse([1,3],[4,5],S).
   Call: (7) concat_reverse([1, 3], [4, 5], _G1444) ? creep
   Call: (8) concat([1, 3], [4, 5], _G1569) ? creep
   Call: (9) concat([3], [4, 5], _G1564) ? creep
   Call: (10) concat([], [4, 5], _G1567) ? creep
   Exit: (10) concat([], [4, 5], [4, 5]) ? creep
   Exit: (9) concat([3], [4, 5], [3, 4, 5]) ? creep
   Exit: (8) concat([1, 3], [4, 5], [1, 3, 4, 5]) ? creep
   Call: (8) reverse([1, 3, 4, 5], _G1444) ? creep
   Call: (9) reverse([1, 3, 4, 5], [], _G1444) ? creep
   Call: (10) reverse([3, 4, 5], [1], _G1444) ? creep
   Call: (11) reverse([4, 5], [3, 1], _G1444) ? creep
   Call: (12) reverse([5], [4, 3, 1], _G1444) ? creep
   Call: (13) reverse([], [5, 4, 3, 1], _G1444) ? creep
   Exit: (13) reverse([], [5, 4, 3, 1], [5, 4, 3, 1]) ? creep
   Exit: (12) reverse([5], [4, 3, 1], [5, 4, 3, 1]) ? creep
   Exit: (11) reverse([4, 5], [3, 1], [5, 4, 3, 1]) ? creep
   Exit: (10) reverse([3, 4, 5], [1], [5, 4, 3, 1]) ? creep
   Exit: (9) reverse([1, 3, 4, 5], [], [5, 4, 3, 1]) ? creep
   Exit: (8) reverse([1, 3, 4, 5], [5, 4, 3, 1]) ? creep
   Exit: (7) concat_reverse([1, 3], [4, 5], [5, 4, 3, 1]) ? creep
S = [5, 4, 3, 1].

4 个答案:

答案 0 :(得分:3)

让我们一点一点地看一下。

您使用过:

concat_reverse([1,3],[4,5],S).

Prolog试图将此统一起来。

concat_reverse(L1,L2,L)

匹配。 L1[1,3]统一,L2[4,5]统一。所以我们看右边:

concat_reverse(L1,L2,L):-concat(L1,L2,LN),reverse(LN,L)

Prolog首先进行深度搜索统一,因此会再次使用统一concat(L1,L2,LN)L1检查L2

让我们一起看看两个concat

concat([], L, L).
concat([H|T1], L2, [H|T]):- concat(T1, L2, T).

如果有帮助,可以将这种模式匹配视为逻辑上的直通。第一个将匹配一个空列表,一些东西,以及相同的东西。这实际上是Prolog所做的深度优先搜索的停止条件。

第二个匹配一个带有头部和尾部的列表,一些具有相同头部和可能不同尾部的列表。在这一点上,我们匹配这个因为统一的工作原理。没有什么重要的是第三个列表。在统一期间,我们将改变“没什么重要”的东西。现在,我们匹配,所以我们遍历下一个concat,传递第一个列表的尾部,第二个列表,另一个“没什么重要”。

在下一个级别,我们匹配相同的条件。我们再次这样做,我们达到了停止状态。我们没什么重要的是第二个清单。因此,我们尝试通过统一从深度优先搜索中爬回来。我们在搜索树中添加了我们上面第一个匹配列表的头部。这是统一的。我们又爬了起来。我们提前一层。这是统一的。您知道什么,我们能够统一第一个concat匹配的concat_reverse字词。

现在我们来看看reverse匹配的concat_reverse部分。这是堆栈跟踪中的Exit(8)/Call(8)。我们再往下走,用这些进行深度优先搜索:

reverse(L, RL):- reverse(L, [], RL).
reverse([], RL, RL).
reverse([H|T], S, RL):- reverse(T, [H|S], RL).

嗯,只有一场比赛,一场有两个任期。但现在我们搜索reverse的两个选项。我们的列表不是空的,所以我们不匹配第一个(再次,这将是我们的停止条件)。我们可以匹配第二个。

我要停止因为继续这不会增加任何新东西。基本上,prolog解释器的工作是匹配,统一和深度优先搜索之一。

希望你已经或者已经有过关于为什么prolog进行深度优先搜索以及为什么这意味着它无法完成反驳(以及你可以做什么作为程序员以防止无限深度搜索)的课程。

基本上,当您编写prolog代码时,您可以像使用归纳证明或递归函数一样编写它。从停止状态开始,然后根据停止条件处理一般情况。根据构图定义事物,例如concat_reverse

我认为你应该看一个更简单的例子来更好地理解统一。这个例子有不必要的复杂性(虽然这些复杂性总是可以帮助你在编写自己的家庭作业的prolog代码时)。尝试单独查看reverse,不要concatconcat_reverse

答案 1 :(得分:3)

免责声明:我不认识你的老师,所以也许你不得不这样做......

告诉你有很多困难,并问为什么代码没有读到:

concat_reverse(L1,L2,L):-reverse(L2,R,L),reverse(L1,[],R).

然后:

seq([]) -->
   [].
seq([E|Es]) -->
   [E],
   seq(Es).

iseq([]) -->
   [].
iseq([E|Es]) -->
   iseq(Es),
   [E].

concat_reverse2(L1,L2,L) :-
   phrase( ( iseq(L2), iseq(L1) ), L).

通常,我不建议使用调试器来理解谓词。

答案 2 :(得分:0)

在阅读(或写作)Prolog时,我想讲一个小故事。

例如,在阅读concat_reverse时,我就是这样解释的: “L是L1和L2的concat_reverse,如果首先LN是L1和L2的串联,其次,L是LN的反转”。

当我阅读concat时,我说: “[]和L的连接是L.” “如果T1和L2的串联为T,则[H | T1]和L2的连续为[H | T]。”

基本上,解释应该以某种方式简化表达式。如果我要写“concat”,我会想“关于concat什么是微不足道的事实?将任何列表连接到空列表是最初的列表。那么[H | T]的一般情况怎么样?将L连接到这是M如果M的头是H而M的其余部分是T到L的串联。换句话说,如果M是[H | RM]并且concat(T,则concat([H | T],L)是M L)是RM。“

至于反向,故事有点复杂,因为没有简单的说“如果......,RL就是L的逆转”。但我们仍然可以提出一个故事。例如,如果L的相应后缀被反转,然后S被连接到结果,我们可以说“S是L的反向后缀(称为RL)。”

因此对于空后缀,我们有反向([],RL,RL) - 如果L的空后缀被反转并且RL被连接到它,则RL是RL的后缀。

在一般情况下,如果L的后缀是[H | T],那么只有当[H | S]也是RL的后缀时,RL的后缀才是S.

这有点令人困惑,特别是当谓词被称为“反向”时。我们必须用一些想象力。但是,如果我们称它为:

concat_of_reversed_suffix_and_S([H|T], S, RL) :- concat_of_reversed_suffix_and_S(T, [H|S], RL).

这有点清楚 - [H | T]的反向是T的反向,后跟H.如果[H | T]是L的后缀而S是后缀或RL,那么T的反向其次是H,其次是S就是RL。我们用这种方式说这句长句:

reverse([H|T], S, RL) :- reverse(T, [H|S], RL).

调试器可以向您显示该过程,但最好让您的头脑中的故事解释它,然后您可以使用调试器进行确认。

在这种情况下,您可以看到:

   Call: (10) reverse([3, 4, 5], [1], _G1444) ? creep
   Call: (11) reverse([4, 5], [3, 1], _G1444) ? creep

[3,4,5]是L的后缀,[1]是RL的后缀。仅当[3,1]也是RL的后缀时才会出现这种情况,将[4,5]留作L的(较小的)后缀。

答案 3 :(得分:0)


concat_reverse(L1,L2,L) :-
        concat_reverse(L1,L2,[],L).

concat_reverse([],[],L,L).
concat_reverse([],[B|R2],L3,L) :-
        concat_reverse([],R2,[B|L3],L).
concat_reverse([A|R1],L2,L3,L) :-
        concat_reverse(R1,L2,[A|L3],L).