Search code examples
prolog

Prolog | findall with result list of another findall


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?


Solution

  • 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'.