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 issues when 'other answers' exist in the same list (i.e. easier to explain with examples).
I have the following knowledge-base facts;
popStar('Jackson',1987,1991).
popStar('Jackson',1992,1996).
popStar('Michaels',1996,2000).
popStar('Newcastle',2000,2007).
popStar('Bowie',2008,2010).
And the following function, calculates the time between dates for a specific fact (as per below).
Predicate (timePeriod/2) -
timePeriod(PS,X) :-
bagof((Name,Start,End),popStar(Name,Start,End),PSs),X is End-Start+1)
Using Bowie as an example; it returns X=3
(which is correct).
However, when there is repetition in the list, with more than one answer available, the predicate just states 'false'. Using the facts 'Jackson' as an example, I want to be able to calculate both of the time periods for both facts; at the same time.
So, if the predicate would work for both of the Jackson facts, the predicate timePeriod
would state X=10
.
Would really appreciate if anyone could suggest what to change in order for this to work correctly.
Thanks.
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:
You probably don't quite understand what
foreach/3
does. I don't think I fully understandforeach/3
either. I know for sure that it is not the same as say: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 betriple(a,b,c)
. For a pair, the idiom isFirst-Second
.So, you can simplify your call to
bagof/3
to this:Once you have a list as above, you need to sum the differences, which would be maybe something like:
And then you can query like this: