Prolog 数据库记录

Prolog Database Recording

我有这个阶乘谓词。

fact(0, 1).
fact(N, F) :- N > 0, 
        N1 is N-1,
        fact(N1, F1),
        F is F1 * N.

如何更改此谓词,以便每次发出查询时,计算结果都存储在数据库中?如果可用,新查询应使用存储的结果。

您要查找的内容通常称为 memoization, but is referred to as tabling in the context of Prolog. Conveniently, there is a library for this called tabling for SWI Prolog。

:- use_module(library(tabling)).
:- table fact/2.

fact(0, 1).
fact(N, F) :- N > 0,
              N1 is N-1,
              fact(N1, F1),
              F is F1 * N.

您可以在调用 fact 之前通过 运行ning trace 验证记忆是否有效。请注意,这两行 :- 被编译器称为指令并且是 运行,因此 运行 在 repl 中使用它们将不起作用。

编辑

如果您不想使用制表库,可以使用 asserta 来完成。这将在数​​据库顶部插入一个事实,就像您自己将它输入到文件中一样。

fact(0, 1).
fact(N, F) :-
              N > 0,
              N1 is N-1,
              fact(N1, F1),
              F is F1 * N,
              asserta(fact(N, F)).

Prolog 将首先看到新谓词并使用它而不是重新计算阶乘。再一次,你可以通过跟踪来检查。

正如jcolemang所说,你要的是tabling/memoization。虽然确实有一些库是推荐的和高效的(不仅是时间方面,而且主要是性能方面)做事方式,但您可能想要自己实现它是可以理解的。

可以使用断言在 prolog 数据库中添加 dynamic 事实,但您需要通过在文件中包含 :-dynamic(fact) 来声明它们,这样您就可以add/remove 事实。

但是,这样做会降低性能。另一种方法是进行查找 table/cache ,该查找将在调用中保留。字典可以使用 global mutable variables(哎呀!)来存储,这比使用动态事实更有效。与往常一样,最好尽量减少直接使用 variables/side-effects 的代码 - 在您的阶乘示例中,您可以简单地使用一个包装器谓词来获取字典并将其放回调用结束时,例如

factorial(N, F):-
get_global_dict(D),
real_factorial(N, F, D, DF),
store_global_dict(DF).