答案 0 :(得分:1)
您发布的Prolog代码的图像显示了一些不寻常或非常旧的Prolog,尤其是在列表中,[H:T]
的使用现在已作为[H|T]
完成,请注意:
的更改到|
,而<=
在:-
中更常见。
要了解Prolog代码,从下至上更容易开始。我不会在此介绍unification或backward 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没有发布有关如何使用它的文档。
在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的线路跟踪输出添加到图像中,然后仅将调用线路放置在树中的相应位置,以便您可以将两者关联起来。我自己做了检查,以确保它是正确的。
如果有一些拼写错误,我将不会感到惊讶,因为我不得不多次重做部分错误以便于理解。希望我实现了这个目标。