如何在SWI-Prolog中列出事实对?

时间:2018-04-03 22:28:49

标签: prolog

假设我有一些事实如下

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)]

2 个答案:

答案 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/3setof/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以查看发生的情况)。第二条规则确保只有在没有更多解决方案要生成时才终止。请注意,该规则包含否定,可能会产生一些不必要的副作用。他们中的大多数都只是通过查看基础术语来过滤掉,但我没有想到,这个解决方案的影响有多糟糕。