データベースに次の事実があるとします。
foo(a, 3).
foo(b, 2).
foo(c, 4).
foo(d, 3).
foo(e, 2).
foo(f, 6).
foo(g, 3).
foo(h, 2).
最小の 2 番目の引数と 2 番目の引数の値を持つすべての最初の引数を収集したいと考えています。初挑戦:
find_min_1(Min, As) :-
setof(B-A, foo(A, B), [Min-_|_]),
findall(A, foo(A, Min), As).
?- find_min_1(Min, As).
Min = 2,
As = [b, e, h].
の代わりにsetof/3
、次を使用できます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].
注意
これは、数値の最小値を探している場合にのみ同じ結果をもたらします。算術式が含まれている場合、結果は異なる場合があります。数値以外が含まれている場合はaggregate(min(...), ...)
、エラーがスローされます!
または、代わりに、キーでソートされた完全なリストを使用できます。
find_min_3(Min, As) :-
setof(B-A, 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].
最後に、質問に:
これをライブラリ(集約)で直接行うことはできますか? 出来るはず…という感じです。
std::partition_point
または、C++ 標準ライブラリのような述語はありますか?または、これを行う簡単な方法はありますか?
編集:
より説明的になるために。(ライブラリ) 述語があったとします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]
).
(名前は好きじゃないけど、今は我慢できる)
それで:
find_min_4(Min, As) :-
setof(B-A, 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].