在 prolog 中断言和使用快速的大型数组

Asserting and using fast, LARGE arrays in prolog

我在 SWI-Prolog 中使用仿函数通过 arg/3 获取随机访问数组。 我正在做的是将样本中的值加载到我创建的仿函数中,并声明该数组以供将来使用。

加载后,随机访问确实是 O(1),正如我使用 time/1 验证的那样。问题是从断言加载仿函数需要很多时间(time/1 表明它与数组的大小成线性关系)。 有什么办法可以将其加快到恒定时间吗?

最小复制代码:

:- dynamic
    current_sample/1.

xrange(L,R,X):-
    L < R,
    ( X = L;
    X1 is L+1, xrange(X1,R,X)
    ).


arraybase_from_list__set_arg_from_list([], _, _).
arraybase_from_list__set_arg_from_list([Head|Tail], I, ResArray):-
    I1 is I+1,
    nb_setarg(I1, ResArray, Head),
    arraybase_from_list__set_arg_from_list(Tail, I1, ResArray).

arraybase_from_list(List, ResArray):-
    length(List, L),
    functor(ResArray, custom_array_data, L),
    arraybase_from_list__set_arg_from_list(List, 0, ResArray ).


test_array_create( N ):- % Creates a dummy array of squares of numbers fromo [0,N)
    findall( X2, (xrange( 0,N,X), X2 is X*X), XList ),
    arraybase_from_list( XList, Arr ),
    assert( current_sample(Arr) ).

test_array_get(I,V):- % Unifies V with Ith element of Current sample
    I0 is I+1,
    current_sample(Arr), % Take turns timing this
    arg( I0, Arr, V ).   % And then timing this

您在使用 current_sample/1 时看到 线性 开销,因为谓词的参数是从数据库 copied 时谓词叫做。

有几种方法可以消除这种线性开销,将其变成𝒪(1).

好方法

一个好的方法是携带整个数组,显式或隐式地遍历所有需要访问它。

您可以使用 半上下文表示法 隐式地执行此操作(请参阅 ),或编写您自己的自定义扩展规则。这类似于 Haskell.

中的 monads

Consider doing this!


更糟糕的方式

一种更糟糕但表面上更简单的方法是使用 全局变量 而不是全局 数据库 来存储术语.

Avoid this!

一些原因:

  • 不可移植
  • 它引入了全局状态,使您的谓词难以阅读和调试
  • 不是比简单地使用额外的参数来携带数组(隐式或显式)更有效
  • 容易出错
  • 等等