Trying to create a predicate (timePeriod/2
) that calculates the time period between two dates for a specific fact. I\'ve managed to do this by myself, but face issu
You probably don't quite understand what foreach/3
does. I don't think I fully understand foreach/3
either. I know for sure that it is not the same as say:
for each x in xs:
do foo(x)
Another thing: "tuples" in Prolog are not what you might expect, coming from a language like Python or Haskell. This: (a,b,c)
is actually this: ','(a,','(b,c))
. Much better is to use a flat term, the generic form would be triple(a,b,c)
. For a pair, the idiom is First-Second
.
So, you can simplify your call to bagof/3
to this:
?- 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].
Once you have a list as above, you need to sum the differences, which would be maybe something like:
periods_total(Ps, T) :-
maplist(period_length, Ps, Ls),
sum_list(Ls, T).
period_length(From-To, Length) :-
Length is To - From + 1.
And then you can query like this:
?- 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 has a nice library to handle aggregation: it builds upon standard 'all solutions' predicates like findall/3,setof/3,bagof/3, so you should first grasp the basic of these (as Boris explained in his answer). With the library, a single query solves your problem:
timePeriod(PS,X) :-
aggregate(sum(P), B^E^(popStar(PS,B,E),P is E-B+1), X).