如何在递归Prolog中将数据插入列表
试图找出如何返回包含a到B的祖先的列表。例如,我有以下事实:如何在递归Prolog中将数据插入列表,prolog,tailrecursion-modulo-cons,Prolog,Tailrecursion Modulo Cons,试图找出如何返回包含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,he
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).
任何帮助都将不胜感激 根据您的方法,您和其他许多开始使用Prolog的人一样,希望将Prolog编程作为一种“命令式语言” 在Prolog中,不能重新分配变量。如果您编写
L=[]
,则这意味着除非您回溯,L
将始终是空列表。因此,稍后调用L=[P|Old]
将导致false
,因为不关联将永远不会产生[]
和[||]
相等的结果
因此,您不能通过先将列表初始化为[]
,然后再“更改”列表来“创建”列表,因为更改是(或者应该是)不可能的。有一些值得注意的例外情况(如使用assert/1
添加事实,但这些通常是“糟糕的设计”)
在实现谓词之前,最好先设计一个归纳定义,指定要实现的逻辑关系。然后您可以将此定义转换为谓词
这里的归纳定义可能如下所示:
ancestorList(X, Z, ___):-
___.
ancestorList(X, Z, ___) :-
parent(X, Y),
___.
X
和Z
的ancestorList(X,Z,L)
是[X]
给定的父(X,Z)
保持;及X
和Y
的ancestorList(X,Y,L)
是一个以X
给定的parent(X,Y)
hols开头的列表,列表的其余部分是Y
到Z
的ancestorList/3
ancestorList(X, Z, ___):-
___.
ancestorList(X, Z, ___) :-
parent(X, Y),
___.
带有仍需填写的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
如果没有无限的父链/2
链,我们知道这个程序不会陷入无限循环,如果两个给定的循环之间没有父链,那么最终会失败。如果它必须保持这一点
ancestorList(约翰,海伦,L):-L=[paul,henry],L=[paul |[henry]]。
那么它也必须认为,
ancestorList(保罗,海伦,L):-L=[henry],L=[henry |[]]以及,
安切斯托利斯(亨利,海伦,L):-L=[]。
但我们也知道
ancestorList(亨利,海伦,L):-父母(亨利,海伦,L=[]。
因此我们知道
%父、子、列表
安切斯托利斯(亨利,海伦,L):-父母(亨利,海伦),L=[]。
%祖先、后代、名单
安切斯托利斯(保罗,海伦,L):-父母(保罗,亨利),L=[Paul | T],
安切斯托名单(亨利、海伦、T)。
这将创建一个几乎是您想要的列表。您可以通过更改上述定义中的一个名称来实现这一点。在遵循祖先
谓词的同时,对您的代码进行最小的编辑修复
%(*祖先(X,Y):-父(X,Y)。*)
%(*祖先(X,Y):-父(X,Z),祖先(Z,Y)。*)
祖先列表(X,Y,L):-parent(X,Y),L=[]。
祖先列表(X,Y,L):-parent(X,Z),L=[Z | Next],祖先列表(Z,Y,Next)。
Prolog构建列表的方式是自顶向下的,而不是像大多数其他函数式语言那样自下而上(它也可以这样做,但自顶向下更简洁、更高效)。因此,我们确实在正在构建的列表顶部“插入”了值Z
,递归调用祖先列表(Z,Y,Next)
完成了Next
列表,直到以[]
结束的基本情况出现,从而创建了列表
[Z1,Z2,Z3,…,ZN]
[Z1 |[Z2 |[Z3 |...[ZN |[].]]
Next1
下一步。。。。
下一个1%(N-1)下一个
下一个
在N
递归调用之后。列表本身不是从上一次递归调用中“返回”的,而是在第一次调用时设置的,其余的递归调用将逐个“设置”(真正统一)其元素
另见:
所以其他“分支”被忽略了(永远不会结束到Y
)的父关系?非常感谢。我用错误的顺序得到了答案。如何在每个列表的后面插入数据?如果我使用函数L=[P | L],这意味着每次找到的新父项都会插入列表的开头。我尝试使用append,但append(L,[P],L)不起作用。在最后一个代码中,您只需更改一个名称。仔细看,你想在列表中选哪一个。上面还有一个具体的例子。我可以告诉你,但最好你自己看看。你确实意识到,henry
是一段具体的数据,一个原子(也是paul
的孩子),而henry
(或paul
)是一个逻辑变量,对吗?只要重命名一致,您可以随意重命名逻辑变量。但是我使用了这些名字,因为它们遵循了顶部显示的具体示例。啊哈,我明白了,我遗漏的部分是我更改了两个名字,使插入的序列顺序相反,我使用了parent(亨利,保罗),而不是parent(保罗,亨利)。非常感谢