Prolog 多个事实的聚合解

Prolog 多个事实的聚合解,prolog,duplicates,aggregate,Prolog,Duplicates,Aggregate,正在尝试创建一个谓词(timePeriod/2),用于计算特定事实的两个日期之间的时间段。我自己设法做到了这一点,但当“其他答案”出现在同一个列表中时,我会面临一些问题(例如,更容易用例子解释) 我有以下知识基础事实 popStar('Jackson',1987,1991). popStar('Jackson',1992,1996). popStar('Michaels',1996,2000). popStar('Newcastle',2000,2007). popStar('Bowi

正在尝试创建一个谓词(
timePeriod/2
),用于计算特定事实的两个日期之间的时间段。我自己设法做到了这一点,但当“其他答案”出现在同一个列表中时,我会面临一些问题(例如,更容易用例子解释)

我有以下知识基础事实

 popStar('Jackson',1987,1991).
 popStar('Jackson',1992,1996).
 popStar('Michaels',1996,2000).
 popStar('Newcastle',2000,2007).
 popStar('Bowie',2008,2010).
下面的函数计算特定事实的日期间隔时间(如下所示)

谓词(时间段/2)

 timePeriod(PS,X) :-
    bagof((Name,Start,End),popStar(Name,Start,End),PSs),X is End-Start+1)
以鲍伊为例;它返回
X=3
(这是正确的)

但是,当列表中有重复项且有多个答案可用时,谓词只声明“false”。以事实“Jackson”为例,我希望能够计算两个事实的两个时间段;同时,

因此,如果谓词对这两个Jackson事实都有效,那么谓词
timePeriod
将声明
X=10

如果有人能建议如何更改以使其正常工作,我将不胜感激


谢谢

您可能不太了解
foreach/3
的功能。我想我也不完全理解每个人的想法。我确信它与我们说的不一样:

for each x in xs:
    do foo(x)
另一件事:Prolog中的“元组”不是您所期望的,它来自Python或Haskell之类的语言。这个:
(a,b,c)
实际上是:
,(a),(b,c))
。更好的方法是使用一个简单的术语,一般形式是
三重(a,b,c)
。对于一对,习惯用法是
First Second

因此,您可以将对
bagof/3
的调用简化为:

?- bagof(From-To, pop_star(Name, Start, End), Ts).
Name = 'Bowie',
Ts = [2008-2010] ;
Name = 'Jackson',
Ts = [1987-1991, 1992-1996] ;
Name = 'Michaels',
Ts = [1996-2000] ;
Name = 'Newcastle',
Ts = [2000-2007].
一旦你有了如上所述的列表,你就需要对差异进行汇总,这可能类似于:

periods_total(Ps, T) :-
    maplist(period_length, Ps, Ls),
    sum_list(Ls, T).

period_length(From-To, Length) :-
    Length is To - From + 1.
然后您可以这样查询:

?- bagof(From-To, pop_star('Jackson', From, To), Ps), periods_total(Ps, T).
Ps = [1987-1991, 1992-1996],
T = 10.

?- bagof(From-To, pop_star(Name, From, To), Ps), periods_total(Ps, T).
Name = 'Bowie',
Ps = [2008-2010],
T = 3 ;
Name = 'Jackson',
Ps = [1987-1991, 1992-1996],
T = 10 ;
Name = 'Michaels',
Ps = [1996-2000],
T = 5 ;
Name = 'Newcastle',
Ps = [2000-2007],
T = 8.

您可能不太了解
foreach/3
的功能。我想我也不完全理解每个人的想法。我确信它与我们说的不一样:

for each x in xs:
    do foo(x)
另一件事:Prolog中的“元组”不是您所期望的,它来自Python或Haskell之类的语言。这个:
(a,b,c)
实际上是:
,(a),(b,c))
。更好的方法是使用一个简单的术语,一般形式是
三重(a,b,c)
。对于一对,习惯用法是
First Second

因此,您可以将对
bagof/3
的调用简化为:

?- bagof(From-To, pop_star(Name, Start, End), Ts).
Name = 'Bowie',
Ts = [2008-2010] ;
Name = 'Jackson',
Ts = [1987-1991, 1992-1996] ;
Name = 'Michaels',
Ts = [1996-2000] ;
Name = 'Newcastle',
Ts = [2000-2007].
一旦你有了如上所述的列表,你就需要对差异进行汇总,这可能类似于:

periods_total(Ps, T) :-
    maplist(period_length, Ps, Ls),
    sum_list(Ls, T).

period_length(From-To, Length) :-
    Length is To - From + 1.
然后您可以这样查询:

?- bagof(From-To, pop_star('Jackson', From, To), Ps), periods_total(Ps, T).
Ps = [1987-1991, 1992-1996],
T = 10.

?- bagof(From-To, pop_star(Name, From, To), Ps), periods_total(Ps, T).
Name = 'Bowie',
Ps = [2008-2010],
T = 3 ;
Name = 'Jackson',
Ps = [1987-1991, 1992-1996],
T = 10 ;
Name = 'Michaels',
Ps = [1996-2000],
T = 5 ;
Name = 'Newcastle',
Ps = [2000-2007],
T = 8.
SWI Prolog有一个易于处理的聚合:它基于标准的“所有解决方案”谓词,如findall/3、setof/3、bagof/3,因此您应该首先掌握这些谓词的基本知识(正如Boris在回答中所解释的)。使用该库,只需一个查询即可解决您的问题:

timePeriod(PS,X) :-
    aggregate(sum(P), B^E^(popStar(PS,B,E),P is E-B+1), X).
SWI Prolog有一个易于处理的聚合:它基于标准的“所有解决方案”谓词,如findall/3、setof/3、bagof/3,因此您应该首先掌握这些谓词的基本知识(正如Boris在回答中所解释的)。使用该库,只需一个查询即可解决您的问题:

timePeriod(PS,X) :-
    aggregate(sum(P), B^E^(popStar(PS,B,E),P is E-B+1), X).

事实上,
foreach/3
就在这个库中。事实上,
foreach/3
就在这个库中。如果您使用的是一个特定于swi prolog的库(
library(aggregate)
),那么您至少应该在那里留下prolog标记,甚至swi prolog如果您正在使用一个特定于swi prolog的库(
library(aggregate)
),那么至少应该保留prolog标记,甚至swi prolog