【发布时间】:2015-02-03 16:37:40
【问题描述】:
鉴于数据库中的以下事实:
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].
注意
如果我正在寻找 数字 的最小值,这只会给出相同的结果。如果涉及算术表达式,结果可能会有所不同。如果涉及到非数字,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].
最后,问题:
我可以直接使用库(聚合)吗?感觉应该是可以的....
或者C++标准库中有没有像
std::partition_point这样的谓词?或者有更简单的方法吗?
编辑:
为了更具描述性。假设有一个(库)谓词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].
【问题讨论】:
-
“直接执行”是什么意思,C++ 是如何发挥作用的?
-
@ScottHunter “直接”意味着有一种方法可以调用 library(aggregate) 中的一个库谓词来执行此操作,但我太笨了,无法弄清楚。提到了 C++,因为链接算法的作用与我的
min_prefix/3非常相似,但以更通用的方式。 -
@ScottHunter 查看我的编辑,了解提及
partition_point背后的理由。 -
@DanielLyons:如果看到相应的 SQL 查询,我们会非常感兴趣。毕竟 SQL 查询通常 - 并不紧凑 - 但至少没有不必要的概念。我会为此悬赏。
-
@Boris:谢谢!顺便说一句,我有 2 个开放的赏金是很现实的(第三个只是为了感谢某人)。特别是明天结束的那个!!
标签: prolog backtracking aggregates prolog-setof meta-predicate