从事实中返回名称列表

时间:2015-12-14 19:17:07

标签: prolog

我正在学习prolog,我正在尝试从我所做的声明中返回一个名单。

示例:

person(sam). 
person(tom). 
person(holly).

我想要回复任何亲自宣布的人的姓名。我试过这样做:

people([]).
people([X | XS]) :-
    person(X),
    people(XS).

它有效,它将sam添加到列表中,然后无限地添加sam而不是切换到tom,然后holly然后结束。有人能指出我正确的方向吗?

1 个答案:

答案 0 :(得分:5)

自定义实施

您可以使用member/2和累加器来解决此问题:

people(L) :-
    people([],L).
people(L,[X|R]) :-
    person(X),
    \+member(X,L),
    people([X|L],R).
people(L,[]) :-
    \+ (person(X),\+ member(X,L)).

或者,如果您知道如何使用 cut !),则可以使用@CapelliC's version

people(L) :-
    people([],L).
people(L,[X|R]) :-
    person(X),
    \+member(X,L),
    !,
    people([X|L],R).
people(L,L).

因此,每次您查找person/1 X时,它都不是L的成员。如果您再也找不到此人,则选择最后一个条款。在这种情况下,空列表[]向后传播,对于调用堆栈中的每个元素,前面添加了特定的X

使用findall/3内置

遵循ISO标准的Prolog变体有内置findall/3

findall(+Template, :Goal, -Bag)

您可以按如下方式使用它:

  • Template是您希望获得的数据的仿函数(可以是变量),X;
  • Goal是应该满足的谓词(或谓词列表等)。 Prolog将在内部致电Goal;和
  • Bag是输出:结果列表。

如果您使用它:

people(L) :-
    findall(X,person(X),L).

它将生成所有person/1 s的列表:

?- findall(X,person(X),L).
L = [sam, tom, holly].

还有其他高阶谓词可以保证唯一性等。

语义差异

请注意,findall/3和我们自己的people/1方法在语义上并不相同。实际上,如果您的数据库包含两次,那么它将是findall/3的两倍。此外,我们自己的people/1将按各种可能的顺序枚举列表。