假设我有一些事实如下
person(jessica,19,usa).
person(james,18,uk).
person(eric,34,italy).
person(jake,24,france).
如何创建一个谓词,创建一个包含所有名称及其相应国家/地区的大型列表,如下所示:
?-filter(L).
L=[(jessica,usa),(james,uk),(eric,italy),(jake,france)]
答案 0 :(得分:3)
最好的解决方案是:
?- bagof((P,C), Age^person(P,Age,C), People).
People = [(jessica, usa), (james, uk), (eric, italy), (jake, france)].
这为您提供了与findall/3
相同的结果,因为findall/3
隐含地假定模板中不存在的所有变量的存在量化((P,C)
是模板)。在您的情况下,您只有一个年龄变量。请注意如果您不包含以下内容会发生什么:
?- bagof((P,C), person(P,_,C), People).
People = [(james, uk)] ;
People = [(jessica, usa)] ;
People = [(jake, france)] ;
People = [(eric, italy)].
这里发生了什么?第二个参数的值在每个解决方案中都是相同的,因为我们没有通知bagof/3
我们并不关心它绑定的内容,即使它只受一件事的约束。 bagof/3
和setof/3
(但不是findall/3
)的这个属性有时会非常有用,所以我倾向于使用bagof/3
而不是findall/3
只需要标记一个或两个变量。
如果我们将另一个年龄相同的人添加到数据库中,那就更明显了:
person(janet,18,australia).
?- bagof((P,C), person(P,Age,C), People).
Age = 18,
People = [(james, uk), (janet, australia)] .
?- bagof((P,C), person(P,_,C), People).
People = [(james, uk), (janet, australia)] ;
答案 1 :(得分:3)
假设person/3
已经基础并终止,您可以在不使用setof的情况下实现它:
notin(_, []).
notin(X, [Y|Ys]) :-
dif(X,Y),
notin(X,Ys).
lt_list(_, []).
lt_list(X, [Y|Ys]) :-
X @< Y,
lt_list(X,Ys).
f( [ Name-Location | Rest], Acc) :-
person(Name, _, Location),
lt_list( Name-Location, Acc ),
f(Rest, [Name-Location | Acc]).
f( [], Acc) :-
\+ (person(Name,_,Location), notin(Name-Location,Acc)).
当我们查询f
时,我们会得到我们的解决方案:
?- f(Xs,[]).
Xs = [jessica-usa, james-uk, jake-france, eric-italy] ;
false.
我使用X-Y
代替(X,Y)
以提高可读性。谓词notin
描述了一个未包含在列表中的元素,lt_list
描述了一个元素,它比标准术语顺序中的列表中的任何内容都要小。
这个想法是第一条规则产生了我尚未见过的人。使用术语订单可确保我们不会生成列表的所有排列(尝试将lt_list
替换为notin
以查看发生的情况)。第二条规则确保只有在没有更多解决方案要生成时才终止。请注意,该规则包含否定,可能会产生一些不必要的副作用。他们中的大多数都只是通过查看基础术语来过滤掉,但我没有想到,这个解决方案的影响有多糟糕。