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

时间:2018-10-07 04:14:57

标签: prolog

试图弄清楚如何返回一个包含人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),它将返回XY之间的祖先列表。 例如,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).

任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:2)

根据您的方法,您-像许多其他开始在Prolog中工作的人一样-旨在以Prolog作为“命令式语言”进行编程。

在Prolog中,您不能重新分配变量。如果您写L = [],则意味着除非您 backtrack ,否则L将始终为空列表。因此,稍后调用L = [P|Old]会导致false,因为取消固定永远不会使[][_|_]相等。

因此,您不能通过首先将其初始化为[]然后再“更改”它来“创建”列表,因为更改是不可能的(或者应该这样做)。有一些值得注意的例外(例如使用assert/1添加事实,但通常是“错误的设计”。)

在实现谓词之前,最好先设计一个归纳定义,该定义指定要实现的逻辑关系。然后,您可以将此定义转换为谓词。

这里的归纳定义如下:

  1. ancestorList(X, Z, L)持有的情况下,两个人XZ的{​​{1}}为[X];和
  2. 两个人parent(X, Z)ancestorList(X, Y, L)中的X是一个以Y开头的X开头的列表,其余列表是parent(X, Y)中的ancestorList/3Y

一旦有了这个归纳定义,我们就可以将其翻译成代码。的“骨架”如下所示:

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 )。

另请参阅: