试图弄清楚如何返回一个包含人A到人B的祖先的列表。例如,我有以下事实:
parent(john,paul).
parent(paul,henry).
parent(henry,helen).
我可以使用以下代码找到Y的祖先
ancestor(X,Y):-parent(X,Y).
ancestor(X,Y):-parent(X,Z), ancestor(Z,Y).
我想有一个函数list(X,Y,L)
,它将返回X
和Y
之间的祖先列表。
例如,List(john,helen,L)
将返回L = [paul, henry]
基于前面的代码,我知道Z是所需的值。但是我不知道如何将这些值插入列表并返回。
我尝试了此操作,但未按预期工作:
list([]).
ancestorList(X,Y,L):- parent(X,Y).
ancestorList(X,Y,L):- parent(P,Y), list(Old), L = [P | Old], ancestorList(X,P,L).
任何帮助将不胜感激。
答案 0 :(得分:2)
根据您的方法,您-像许多其他开始在Prolog中工作的人一样-旨在以Prolog作为“命令式语言”进行编程。
在Prolog中,您不能重新分配变量。如果您写L = []
,则意味着除非您 backtrack ,否则L
将始终为空列表。因此,稍后调用L = [P|Old]
会导致false
,因为取消固定永远不会使[]
和[_|_]
相等。
因此,您不能通过首先将其初始化为[]
然后再“更改”它来“创建”列表,因为更改是不可能的(或者应该这样做)。有一些值得注意的例外(例如使用assert/1
添加事实,但通常是“错误的设计”。)
在实现谓词之前,最好先设计一个归纳定义,该定义指定要实现的逻辑关系。然后,您可以将此定义转换为谓词。
这里的归纳定义如下:
ancestorList(X, Z, L)
持有的情况下,两个人X
和Z
的{{1}}为[X]
;和parent(X, Z)
和ancestorList(X, Y, L)
中的X
是一个以Y
开头的X
开头的列表,其余列表是parent(X, Y)
中的ancestorList/3
至Y
。一旦有了这个归纳定义,我们就可以将其翻译成代码。的“骨架”如下所示:
Z
ancestorList(X, Z, ___):-
___.
ancestorList(X, Z, ___) :-
parent(X, Y),
___.
仍然需要填写。
鉴于没有 infinite ___
链,我们知道该程序不会陷入无限循环,并且如果两个给定链之间没有父母链,最终该程序将失败一个。
答案 1 :(得分:2)
如果必须保留那个
ancestorList( john, helen, L) :- L = [paul, henry], L = [paul | [henry ] ].
那么它也必须保持
ancestorList( paul, helen, L) :- L = [ henry], L = [henry | [] ] . % and,
ancestorList( henry, helen, L) :- L = [] .
但是我们也知道
ancestorList( henry, helen, L) :- parent( henry, helen), L = [] .
因此我们知道
% Parent, Child, List
ancestorList( Henry, Helen, L) :- parent( Henry, Helen), L = [] .
% Ancestor, Descendant, List
ancestorList( Paul, Helen, L) :- parent( Paul, Henry), L = [ Paul | T ] ,
ancestorList( Henry, Helen, T ) .
这将创建几乎是您想要的列表。您可以通过在上述定义中更改一个名称来做到这一点。
答案 2 :(得分:0)
在您确实希望遵循ancestor
谓词的同时,对代码进行最小编辑修正
% (* ancestor(X,Y) :- parent(X,Y). *)
% (* ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y). *)
ancestor_list(X,Y,L) :- parent(X,Y), L = [].
ancestor_list(X,Y,L) :- parent(X,Z), L = [Z | Next], ancestor_list(Z,Y,Next).
Prolog 构建列表的方式是自上而下的,不仅像大多数其他功能语言一样自下而上(它也可以做到 ,但自上而下更整洁,更有效率)。因此,我们确实将Z
值“插入”了正在构建的列表L = [Z | Next]
的 top 中,并且递归调用ancestor_list(Z,Y,Next)
完成了{{1 }}列出,直到基本情况按原样以Next
结尾,从而创建列表
[]
在[Z1 , Z2 , Z3 , ...., ZN ]
[Z1 | [Z2 | [Z3 | .... [ZN | []] .... ]]]
Next1
Next2 ....
NextN_1 % (N-1)th Next
NextN
递归调用之后。列表本身不是从上次递归调用中“返回”的,而是由最初的 调用和其他的 setup 设置的。递归调用会完成其元素“设置”(实际上是 unifying )。
另请参阅: