从事实中返回名称列表

Yop*_*per 3 prolog

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

例:

person(sam). 
person(tom). 
person(holly).
Run Code Online (Sandbox Code Playgroud)

我想要归还任何亲自申报的人的姓名.我试过这样做:

people([]).
people([X | XS]) :-
    person(X),
    people(XS).
Run Code Online (Sandbox Code Playgroud)

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

Wil*_*sem 5

自定义实施

例如,您可以使用a member/2和accumulator 来解决此问题:

people(L) :-
    people([],L).
people(L,[X|R]) :-
    person(X),
    \+member(X,L),
    people([X|L],R).
people(L,[]) :-
    \+ (person(X),\+ member(X,L)).
Run Code Online (Sandbox Code Playgroud)

或者,如果你知道如何使用cut(!),你可以使用@ CapelliC的版本:

people(L) :-
    people([],L).
people(L,[X|R]) :-
    person(X),
    \+member(X,L),
    !,
    people([X|L],R).
people(L,L).
Run Code Online (Sandbox Code Playgroud)

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

使用findall/3内置

然而,遵循ISO标准的Prolog变体有内置findall/3:

findall(+Template, :Goal, -Bag)
Run Code Online (Sandbox Code Playgroud)

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

  • Template是你想要获得的数据的仿函数(这可以是变量)X;
  • Goal是谓词(或谓词的列表等),应该是满意的.Prolog将在内部致电Goal; 和
  • Bag 是输出:结果列表.

如果你使用这个:

people(L) :-
    findall(X,person(X),L).
Run Code Online (Sandbox Code Playgroud)

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

?- findall(X,person(X),L).
L = [sam, tom, holly].
Run Code Online (Sandbox Code Playgroud)

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

语义差异

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

  • @CapelliC:通常我都会提出同样的建议,但是由于切割会产生多方向谓词的副作用,所以我开始不经常使用它们.我已将您的版本包含在答案中(当然还有对评论的引用). (2认同)