Prolog 在序言中对事实强加命令

Prolog 在序言中对事实强加命令,prolog,Prolog,假设f(X)是一个动态事实,可以asserted和retracted,并且假设X始终是数字。现在,假设执行次数最多的查询是关于查找f(X)这样的最小值。在SWI序言中,我可以写: min_f(R) :- aggregate(min(X), f(X), R). 但是,显然,这总是导致Prolog对所有事实执行线性搜索。现在,假设将有大量(例如1000000)此类事实。因为我事先知道我会经常执行min\u f/1: 我是否可以对f/1进行排序,以便发动机可以在O(1)中找到最小值 我可以明确地断

假设
f(X)
是一个动态事实,可以
assert
ed和
retract
ed,并且假设
X
始终是数字。现在,假设执行次数最多的查询是关于查找
f(X)
这样的最小值。在SWI序言中,我可以写:

min_f(R) :- aggregate(min(X), f(X), R).
但是,显然,这总是导致Prolog对所有事实执行线性搜索。现在,假设将有大量(例如1000000)此类事实。因为我事先知道我会经常执行
min\u f/1

  • 我是否可以对
    f/1
    进行排序,以便发动机可以在O(1)中找到最小值
  • 我可以明确地
    断言
    一个包含所有事实的最小堆,然后偷看头部;事实是否可以隐式存储在min堆中
我对Prolog方言没有任何限制,因此任何可选的Prolog实现都可以。

:-dynamic(f/1)。
:- dynamic(f/1).

min_f(X) :-
    f(X),
    !.
assert_f(X) :- 
    min_f(Min),
    Min<X,
    assertz(f(X)),
    !.
assert_f(X) :-
    asserta(f(X)).
最小f(X):- f(X), !. 断言f(X):- 最小值(最小值),
Min可以快速提供最小值或最大值的数据结构的替代方法是使用模式导向的表格。在定向表格模式中,可以指定所需的聚合函数:

f(5).
f(4).
f(6).

:- table min_f(min).
min_f(X) :- f(X).
最小值只计算一次,下面是一个示例查询。适用于SWI Prolog,自1.4.0版起,也适用于Jekejeke Prolog:

?- min_f(X).
X = 4.

从理论上讲,这取决于制表实现,它如何计算最小值。目前还有一些发展,即增量制表,这将允许使f/1动态,然后跟踪更改。

我建议您只需使用SICStus中提供的
库(堆)
,SWI等维护一个显式堆,您可以通过它执行线程,并且您将始终拥有对最小值的O(1)访问权限。它还简化了对代码的调试和推理,因为您不需要像修改全局数据库那样依赖于副作用和隐式状态。而且,它很可能更快。我支持@mat的建议。与将数据库与
assert
retract
一起使用相比,逻辑变量通常是一种更干净的维护状态的方法。对于您的用例,
库(堆)
似乎是一个很好的匹配。即使只是保留一个排序的值列表(有或没有重复项)也可能是一个可接受的选择:访问头部是固定时间,即使插入不是。我现在正在玩SWI Prolog堆。显然,当解释器变得足够大(约100.000个条目)时,它会使解释器出现segfault。如果您确实遇到segfault,请在开发版本上提交一个问题。如果您只得到一个全局堆栈溢出,您可以增加全局堆栈大小,例如使用
-G2G
在命令行上将全局堆栈限制设置为2GB(这至少需要一个64位体系结构)。快速测试:
?-length(u,Exp),N为2^Exp,length(Ls,N),numlist(1,N,Ps),pairs\u-keys\u值(pairs,Ps,u),list\u-to\u-heap(Pairs,H)。
在64位系统上使用默认SWI设置(使用~270MB全局堆栈),这将上升到
2^20
,从而创建一个包含100多万个元素的堆而不会出现问题。当然,根据堆元素的具体结构,您可能需要更多的空间。您还可以“外包”其中至少有一部分可以连接到全局数据库进行快速查找。尽管如此,堆作为显式参数可用还是不错的,这样可以简化测试等。很高兴看到简单易懂的用于列表的用例。