Given the following facts in the database:
foo(a, 3). foo(b, 2). foo(c, 4). foo(d, 3). foo(e, 2). foo(f, 6). foo(g, 3). foo(h, 2).
I want to collect all the first arguments that have the smallest second argument, plus the value of the second argument. First try:
find_min_1(Min, As) :- setof(BA, foo(A, B), [Min-_|_]), findall(A, foo(A, Min), As). ?- find_min_1(Min, As). Min = 2, As = [b, e, h].
Instead of setof/3 I could use aggregate/3 :
find_min_2(Min, As) :- aggregate(min(B), A^foo(A, B), Min), findall(A, foo(A, Min), As). ?- find_min_2(Min, As). Min = 2, As = [b, e, h].
NB
This only gives the same results if I am looking for a minimum number . If an arithmetic expression is involved, the results may be different. If not a number is involved, aggregate(min(...), ...) will throw an error!
Or instead, I can use a complete list sorted by key:
find_min_3(Min, As) :- setof(BA, foo(A, B), [Min-First|Rest]), min_prefix([Min-First|Rest], Min, As). min_prefix([Min-First|Rest], Min, [First|As]) :- !, min_prefix(Rest, Min, As). min_prefix(_, _, []). ?- find_min_3(Min, As). Min = 2, As = [b, e, h].
Finally, to the question (s):
Can I do this directly using the library (aggregate)? Looks like it should be possible ....
Or is there a predicate like std::partition_point from the C ++ standard library?
Or is there an easier way to do this?
EDIT:
To be more visual. Say there was a predicate (library) partition_point/4 :
partition_point(Pred_1, List, Before, After) :- partition_point_1(List, Pred_1, Before, After). partition_point_1([], _, [], []). partition_point_1([H|T], Pred_1, Before, After) :- ( call(Pred_1, H) -> Before = [H|B], partition_point_1(T, Pred_1, B, After) ; Before = [], After = [H|T] ).
(I don't like the name, but we can live with it for now)
Then:
find_min_4(Min, As) :- setof(BA, foo(A, B), [Min-X|Rest]), partition_point(is_min(Min), [Min-X|Rest], Min_pairs, _), pairs_values(Min_pairs, As). is_min(Min, Min-_). ?- find_min_4(Min, As). Min = 2, As = [b, e, h].