我正在学习prolog,我正在尝试从我所做的声明中返回一个名单。
示例:
person(sam).
person(tom).
person(holly).
我想要回复任何亲自宣布的人的姓名。我试过这样做:
people([]).
people([X | XS]) :-
person(X),
people(XS).
它有效,它将sam
添加到列表中,然后无限地添加sam
而不是切换到tom
,然后holly
然后结束。有人能指出我正确的方向吗?
答案 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
将按各种可能的顺序枚举列表。