鉴于数据库中的以下事实:
foo(a, 3).
foo(b, 2).
foo(c, 4).
foo(d, 3).
foo(e, 2).
foo(f, 6).
foo(g, 3).
foo(h, 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].