如何在递归Prolog中将数据插入列表

如何在递归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

试图找出如何返回包含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).

任何帮助都将不胜感激

根据您的方法,您和其他许多开始使用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(保罗,亨利)。非常感谢