需要帮助了解Prolog append / 3和inverse / 2以及跟踪输出

时间:2018-12-17 14:50:30

标签: algorithm prolog logic-programming

这是问题

enter image description here

找到值A。


y

这是答案:

enter image description here

请帮助我理解这一点!

1 个答案:

答案 0 :(得分:1)

您发布的Prolog代码的图像显示了一些不寻常或非常旧的Prolog,尤其是在列表中,[H:T]的使用现在已作为[H|T]完成,请注意:的更改到|,而<=:-中更常见。

要了解Prolog代码,从下至上更容易开始。我不会在此介绍unificationbackward chaining,因为要进行到这一级别的详细介绍,将需要一章。

第一个要理解的谓词是append/3。通常,您永远不会看到给出的append代码,因为它是一个内置谓词,但在这里是给出的。

附录/ 3具有全部列出的三个参数。前两个附加在一起形成第三个。

?- append_01([],[],R).
R = [].

?- append_01([a],[],R).
R = [a].

?- append_01([],[a],R).
R = [a].

?- append_01([a],[b],R).
R = [a, b].

但是Prolog谓词可以具有其他modes操作,可以将值绑定到其他编程语言(例如,

)中被视为输入参数的值。
?- append(X,[b],[a,b]).
X = [a] ;
false.

?- append_01([a],Y,[a,b]).
Y = [b].

?- append(X,Y,[a,b]).
X = []    , Y = [a, b] ;
X = [a]   , Y = [b]    ;
X = [a, b], Y = []     ;
false.

或仅可用于验证参数

?- append([a],[b],[a,b]).
true.

?- append([a],[c],[a,b]).
false.

接下来是谓词inverse / 2,在Prolog中通常称为reverse/2,这里再次给出了源代码。

这只是获取一个列表并将其反向,例如

?- inverse([],X).
X = [].

?- inverse([a],X).
X = [a].

?- inverse([a,b],X).
X = [b, a].

然而,此版本的源代码在其他模式下效果不佳,例如

?- inverse(X,[]).
X = [] ;

Action (h for help) ? abort
% Execution Aborted

但是回答这个问题并不重要。


您发布的内容的下一部分是查询执行的痕迹

?- inverse([[1,2,3],[5,4]],A).

为了在您的代码上使用跟踪,由于有一个append / 3的内置谓词,因此我不得不重命名谓词。这是我使用的代码。

inverse([],[]).

inverse([H|T],D) :-
  inverse(T,Z),
  append_01(Z,[H],D).

append_01([],X,X).

append_01([X|L],M,[X|N]) :-
  append_01(L,M,N).

使用SWI-Prolog

设置跟踪

?- visible(+all),leash(-all). 

开始跟踪

trace.

执行查询

[trace] ?- inverse([[1,2,3],[5,4]],A).

返回

   Call: (8) inverse([[1, 2, 3], [5, 4]], _7548)
   Unify: (8) inverse([[1, 2, 3], [5, 4]], _7548)
   Call: (9) inverse([[5, 4]], _7794)
   Unify: (9) inverse([[5, 4]], _7794)
   Call: (10) inverse([], _7794)
   Unify: (10) inverse([], [])
   Exit: (10) inverse([], [])
   Call: (10) append_01([], [[5, 4]], _7802)
   Unify: (10) append_01([], [[5, 4]], [[5, 4]])
   Exit: (10) append_01([], [[5, 4]], [[5, 4]])
   Exit: (9) inverse([[5, 4]], [[5, 4]])
   Call: (9) append_01([[5, 4]], [[1, 2, 3]], _7548)
   Unify: (9) append_01([[5, 4]], [[1, 2, 3]], [[5, 4]|_7792])
   Call: (10) append_01([], [[1, 2, 3]], _7792)
   Unify: (10) append_01([], [[1, 2, 3]], [[1, 2, 3]])
   Exit: (10) append_01([], [[1, 2, 3]], [[1, 2, 3]])
   Exit: (9) append_01([[5, 4]], [[1, 2, 3]], [[5, 4], [1, 2, 3]])
   Exit: (8) inverse([[1, 2, 3], [5, 4]], [[5, 4], [1, 2, 3]])
A = [[5, 4], [1, 2, 3]].

我不会像其他SO Q&A that那样解释跟踪的详细信息。


与使用trace生成的跟踪相比,您发布的跟踪还具有更多的细节,例如bindings(θ)。

要查看绑定,请使用gtrace/0

?- gtrace.
% The graphical front-end will be used for subsequent tracing
true.

然后执行查询

[trace]?- inverse([[1,2,3],[5,4]],A).

,然后按空格键单步执行。您将不得不对其进行试验以了解其工作原理。 AFAIK没有发布有关如何使用它的文档。

enter image description here


在OP评论中:

  

从字母到数字和theta符号的一些替换使我难以理解。

尽管绑定(θ)更特定于logic languages,但数字也可以在基于堆栈的functional languages中看到,请参见De Bruijn index。同样,我不喜欢使用垂直线分隔符(---)来写绑定,而是更喜欢使用{↦3}}中的(↦)。

第1 -4行只是再次说明的源代码。

通常使用跟踪的目的是传达执行(调用)的树结构,但是使用这些行,除非您知道Prolog的工作原理,否则很难看到有树结构。

带有横杠的行旨在帮助您了解发生了什么,但是,如果您仅遵循执行(调用)的流程,那么您可能会发现,正如我所做的那样,它们只会引起混乱,可以忽略。

您在注释中注意到Res(_,_)是指跟踪中的前几行。因此,可以将第6行的Res(5,2)读为第6行,因为第6行是第5行的调用结果,然后调用第2行。

统一或绑定(θ)显示为集合。我不确定上级脚本和子级脚本的编号代表什么,但是它们显然与De Bruijn索引相关联。您将不得不请老师解释上级脚本和下级脚本。

尝试了几次,仅用文本进行解释后,我终于求助于Microsoft Visio,将其用作图形树,它变得更加容易,快捷和准确。

即使不需要,我也将SWI-Prolog的线路跟踪输出添加到图像中,然后仅将调用线路放置在树中的相应位置,以便您可以将两者关联起来。我自己做了检查,以确保它是正确的。

如果有一些拼写错误,我将不会感到惊讶,因为我不得不多次重做部分错误以便于理解。希望我实现了这个目标。

here