Prolog 从事实中返回名称列表

Prolog 从事实中返回名称列表,prolog,Prolog,我正在学习prolog,并试图从我所做的声明中返回一个名称列表 例如: person(sam). person(tom). person(holly). 我想把亲自申报的人的名字还给你。我试着这样做: people([]). people([X | XS]) :- person(X), people(XS). 它的工作原理是,将sam添加到列表中,然后无限地添加sam,而不是切换到tom,然后切换到holly,然后结束。有人能告诉我正确的方向吗?自定义实现 例如,您可以使

我正在学习prolog,并试图从我所做的声明中返回一个名称列表

例如:

person(sam). 
person(tom). 
person(holly).
我想把亲自申报的人的名字还给你。我试着这样做:

people([]).
people([X | XS]) :-
    person(X),
    people(XS).
它的工作原理是,将
sam
添加到列表中,然后无限地添加
sam
,而不是切换到
tom
,然后切换到
holly
,然后结束。有人能告诉我正确的方向吗?

自定义实现 例如,您可以使用a和累加器来解决此问题:

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(
),您可以使用:

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

使用
findall/3
内置 但是,遵循ISO标准的Prolog变体有一个内置的:

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

  • Template
    是您希望获取的数据的函子(可以是变量),这里是
    X
  • 目标是应该满足的谓词(或谓词列表等)。Prolog将在内部调用
    目标
    ;及
  • Bag
    是输出:结果列表
如果您这样使用:

people(L) :-
    findall(X,person(X),L).
它将生成所有
人员/1
s的列表:

?- findall(X,person(X),L).
L = [sam, tom, holly].
还有其他高阶谓词可以保证唯一性,等等

语义差异
请注意,
findall/3
和我们自己的
people/1
方法在语义上并不等价。事实上,如果您的数据库包含一个人两次,那么它将在
findall/3
包中出现两次。此外,我们自己的
人员/1
将按每个可能的顺序枚举列表。

@CommuSoft向您展示如何执行此操作的选项。您所拥有的不起作用的原因是,当输入一个新的谓词子句调用时,Prolog从事实的开头开始。因此,每次递归调用
,都会从事实的开始重新开始。这就是为什么存在
findall/3
。)@卡佩拉:使用切割
在上一条中?通过解释也帮助我理解了,非常感谢!下面是我测试的代码:
人物(L,R):-person(X),\+成员(X,L)!,人([X | L],R)。人(L,L)
@capelical:通常我也会建议这样做,但由于当涉及到多向谓词时,剪切会产生副作用,所以我开始减少使用它们的频率。我已经在答案中包含了你的版本(当然还提到了评论)。
people(L) :-
    findall(X,person(X),L).
?- findall(X,person(X),L).
L = [sam, tom, holly].