序言中的计数器

Counter in prolog

我想在序言中创建一个计数器。

有点像启动它 init/0。 添加 1 increment/0, 和 get_counter/1 之类的东西。获得价值。

但是我不知道如何开始一些事情,如果你 init/0 没有输入如何将某些东西设置为 0。

有人可以给我一些提示,我应该如何尝试这样做吗?

我不是母语人士,所以如果不清楚我的意思,我很抱歉。

我会用 ECLiPSe's non-local variables:

init :- setval(counter, 0).
increment :- incval(counter).
get_counter(V) :- getval(counter, V).

您的实施可能会提供类似的东西。在 SWI-prolog 中,似乎可以使用 nb_setval (不可回溯的 setval)来实现相同的目的。

解决这个问题的一种声明性方法是将其视为两个计数器值之间的关系:一个在增量之前,一个在增量之后。

您可以使用 CLP(FD) 约束来关联两个计数器值:

counter_next(C0, C) :- C0 + 1 #= C.

这样的谓词是完全纯粹的,可以全方位使用

一系列这样的关系描述了计数器的重复递增,将初始值与其最终状态相关联:

?- S0 = 0, counter_next(S0, S1), counter_next(S1, S).
S = 2,
S0 = 0,
S1 = 1

EDIT:假设你走另一条路并设法实现一个 0 元谓词 increment/0,正如你所要求的那样,破坏性地增加全局资源。那么你将遇到严重的声明性问题。例如,递增计数器必须成功,所以我们可以期望看到:

?- increment.
true.

但这意味着原来的查询不再等同于它自己的答案,因为查询:

?- true.
true.

当然不会增加计数器。

这也意味着您不能再孤立地测试和推理您的谓词,而必须始终考虑全局资源。

这反过来会使理解和更正代码中的错误变得更加困难。

因此,我强烈建议你采用声明式的方式来思考这个任务,并明确计数器值递增前后的关系。作为一个额外的好处,您还可以在其他方向使用这些关系,例如:"Which initial counter values, if any, yield a given value when incremented?",或者更一般的:"For which arguments does this relation even hold?"

这是您想要实现的目标:

?- X0 = 0 /* init */, succ(X0, X1) /* inc */, succ(X1, X2) /* inc */.
X0 = 0,
X1 = 1,
X2 = 2.

init只是给变量一个值,递增是用succ/2完成的,getval是隐式的。

但是,正如我在评论中所说,考虑您的用例!如果您想跟踪自己在循环中的深度,使用 succ/2 或什至遵循 .

完全没问题

因此,要计算列表中 foo 的数量:

list_foos([], 0).
list_foos([X|Xs], N) :-
    (   dif(X, foo)
    ->  list_foos(Xs, N)
    ;   list_foos(Xs, N0),
        succ(N0, N) % or: N0 + 1 #= N
    ).

您应该同时尝试 succ(N0, N)N0 + 1 #= N,看看当 list_foos/2 的一个或两个参数不成立时如何使用它们。

但是,如果您出于某种原因需要维护全局状态:例如,您正在动态更改数据库并且需要为 table 生成一个递增的整数键。然后,你应该考虑 . Keep in mind that it is not super easy to write code that runs on any Prolog implementation once you start using "global" variables. One attempt would be to use the predicates for manipulating the database:

:- dynamic foocounter/1.

initfoo :-
    retractall(foocounter(_)),
    assertz(foocounter(0)).

incrfoo :-
    foocounter(V0),
    retractall(foocounter(_)),
    succ(V0, V),
    assertz(foocounter(V)).

然后,您现在可以使用全局状态进行计数(它不需要像您的示例一样使用连词):

?- initfoo.
true.

?- incrfoo.
true.

?- incrfoo.
true.

?- foocounter(V).
V = 2.

这是完全有效的代码,但有很多陷阱,请谨慎使用。