Reputation: 461
How can i get values by an specific condition, and next with these selected elements get values from other facts serie?
I've this code
%code, date, amount
values1('AAA', date(02, 03, 2020), 1000).
values1('AAA', date(31, 03, 2020), 2000).
values1('BBB', date(02, 04, 2020), 1350).
values1('BBB', date(15, 04, 2020), 1500).
values1('CCC', date(23, 05, 2020), 5500).
values1('CCC', date(01, 05, 2020), 750).
values1('DDD', date(06, 05, 2020), 3560).
values1('AAA', date(18, 06, 2020), 4600).
values1('CCC', date(27, 07, 2020), 1200).
%code, percent
values2('AAA', '0.2').
values2('BBB', '0.25').
values2('CCC', '0.55').
values2('DDD', '0.98').
predicate1(Code, Percent) :-
findall(Code, (values1(Code, _, Value), Value > 1000), Code),
findall(Percent, values2(Code, Percent), Percent).
For example in this case the idea it's get Code which amount is bigger than 1000, and when there are selected get the percent from value2, but the Percent list it's return empty. Why it happening that?
This is the return
predicate1(Code, Percent).
Code = ['AAA', 'BBB', 'BBB', 'CCC', 'DDD', 'AAA', 'CCC'],
Percent = []
it may be by the difference between number of elements in list of Code and number of fact of values2 maybe?
Upvotes: 2
Views: 828
Reputation: 9378
The other answers give you solutions with backtracking. If you really want lists (see note below), you need a predicate that can relate "a list of A
elements" and "a list of B
elements" such that each A
-B
pair is in some relation.
SWI-Prolog has maplist/N for this:
predicate1(Codes, Percents) :-
findall(Code, (values1(Code, _, Value), Value > 1000), Codes),
maplist(values2, Codes, Percents).
This gives you your codes and an equal length list of the corresponding percentages:
?- predicate1(Codes, Percents).
Codes = ['AAA', 'BBB', 'BBB', 'CCC', 'DDD', 'AAA', 'CCC'],
Percents = ['0.2', '0.25', '0.25', '0.55', '0.98', '0.2', '0.55'].
Note: Prolog beginners love collecting solutions into lists. This is often not the best way to solve a problem in Prolog. Backtracking can be more natural, simpler, and more efficient. I recommend that you try to resist the urge to collect everything into lists unless really needed.
Edit: As a note on style, if I do use findall/3
, setof/3
, or bagof/3
, I prefer to write an auxiliary predicate that hides all variables that are not relevant to the data I want to collect:
code_above(Code, Limit) :-
values1(Code, _, Value),
Value > Limit.
This way the findall
and setof
calls look the same, which makes it easy to switch between them. They are also (in my opinion) a lot more readable than the Value^Date^
gymnastics that setof
otherwise requires, as illustrated in David Tonhofer's answer:
predicate1(Codes, Percents) :-
findall(Code, code_above(Code, 1000), Codes),
maplist(values2, Codes, Percents).
predicate2(Codes, Percents) :-
setof(Code, code_above(Code, 1000), Codes),
maplist(values2, Codes, Percents).
With this you get rid of duplicates for free:
?- predicate1(Codes, Percents).
Codes = ['AAA', 'BBB', 'BBB', 'CCC', 'DDD', 'AAA', 'CCC'],
Percents = ['0.2', '0.25', '0.25', '0.55', '0.98', '0.2', '0.55'].
?- predicate2(Codes, Percents).
Codes = ['AAA', 'BBB', 'CCC', 'DDD'],
Percents = ['0.2', '0.25', '0.55', '0.98'].
Upvotes: 2
Reputation: 783
predicate1(Code,Percent)
:-
values1(Code,_,Amount) ,
Amount > 1000 ,
values2(Code,Percent)
.
example usage from toplevel :
?- predicate1(Code,Percent).
Code = 'AAA',
Percent = '0.2' ;
Code = 'BBB',
Percent = '0.25' ;
Code = 'BBB',
Percent = '0.25' ;
Code = 'CCC',
Percent = '0.55' ;
Code = 'DDD',
Percent = '0.98' ;
Code = 'AAA',
Percent = '0.2' ;
Code = 'CCC',
Percent = '0.55'.
Upvotes: 2
Reputation: 15316
You have a typo and a (few) logic errors.
The value of Code
goes into a list of Code
: CodeList
. You have variable Code
on first and third position. Use this:
findall(Code, (values1(Code, _, Value), Value > 1000), CodeList).
Run it:
?- findall(Code, (values1(Code, _, Value), Value > 1000), CodeList).
CodeList = ['AAA', 'BBB', 'BBB', 'CCC', 'DDD', 'AAA', 'CCC'].
Duplicate elements are removed by using the predicate setof/3
instead.
However, setof/3
demands that you indicate ("existentially quantify" with the caret notation: Value^Date^
) the variables that shall be invisible outside the inner goal, otherwise backtracking over possible values of Value
and Date
will occur outside of setof/3
:
?- setof(Code, Value^Date^(values1(Code, Date, Value), Value > 1000), CodeList).
CodeList = ['AAA', 'BBB', 'CCC', 'DDD'].
Now you just need to "join" any "code" with the "percentage" ... inside of setof/3
:
?- setof([Code,Percent],
Value^Date^(values1(Code, Date, Value),
Value > 1000,
values2(Code,Percent)),
Set).
Set = [['AAA', '0.2'], ['BBB', '0.25'], ['CCC', '0.55'], ['DDD', '0.98']].
You can pack this into a predicate which backtracks over the setof/3
result:
gimme(Code, Percent) :-
setof([Code,Percent],
Value^Date^(values1(Code, Date, Value),
Value > 1000,
values2(Code,Percent)),
Set),
member([Code,Percent],Set).
Note that the reuse of variables of Code
and Percent
inside setof/3
and in the call to member/2
s well is actually ok: These are not the same variables.
And so:
?- gimme(Code,Percent).
Code = 'AAA',
Percent = '0.2' ;
Code = 'BBB',
Percent = '0.25' ;
Code = 'CCC',
Percent = '0.55' ;
Code = 'DDD',
Percent = '0.98'.
Upvotes: 3