假设有以下这些数据库中的事实:
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].
我可以使用aggregate/3
代替setof/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
只有在寻找一个数字的最小值时才会得到相同的结果。如果涉及算术表达式,结果可能会不同。如果涉及非数字,则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].
最后,关于问题:
我能否直接使用library(aggregate)完成这个操作?感觉应该是可能的...
还是有类似于C++标准库中
std::partition_point
这样的谓词吗?还是有更简单的方法来完成这个操作吗?
编辑:
为了更加详细,假设有一个(library) predicate 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].
min_prefix/3
非常相似,但更加通用。 - user1812457