【问题标题】:count the number of calls of a clause计算子句的调用次数
【发布时间】:2012-07-06 14:55:35
【问题描述】:

我有一个类似如下的子句:

lock_open:-
        条件组合(X),
        等于(X,[8,6,5,3,6,9]),!,
        打印(X)。

此子句成功。但我想知道在equal(X,[8,6,5,3,6,9]) 变为真之前调用了多少次conditional_combination()。该程序是通过遵循一些规则来生成一个排列。而且我需要生成多少排列才能获得像 865369 这样的特定值。

【问题讨论】:

    标签: recursion count prolog clause


    【解决方案1】:

    你真正想要的是稍微不同的东西:你想计算一个目标的答案数量(到目前为止)。

    下面的谓词call_nth(Goal_0, Nth)call(Goal_0) 一样成功,但有一个附加参数表明找到的答案是第n 个答案。此定义高度特定于 SWI 或 YAP。不要不要在你的一般程序中使用nb_setarg/3 之类的东西,而是将它们用于封装良好的情况,就像这个一样。甚至在 对于这两个系统,这些构造的确切含义对于一般情况没有很好的定义。 Here is a definition for SICStus.

    call_nth(Goal_0, C) :- State = count(0,_), % 注意额外的参数,它仍然是一个变量 目标_0, arg(1,状态,C1), C2 是 C1+1, nb_setarg(1,状态,C2), C = C2。

    Eclipse 提供了更强大的抽象:

    call_nth(Goal_0, Nth) :-
       shelf_create(counter(0), CounterRef),
       call(Goal_0),
       shelf_inc(CounterRef, 1),
       shelf_get(CounterRef, 1, Nth).
    
    ?- call_nth((1,5,I),Nth). 我 = 第 N, 第 N = 1 ; 我 = 第 N,第 N = 2; 我 = 第 N,第 N = 3; 我=第N次,第N次= 4; 我 = 第 N 个,第 N 个 = 5。

    所以简单地把它包裹起来:

    lock_open :- call_nth(conditional_combination(X), Nth), X = [8,6,5,3,6,9], !, ……

    【讨论】:

    • 我看到了一种使用这种原语在时间空间中进行聚合器 O(N)的方法。谢谢!
    • 不得不重新考虑limit/2和offset/2的实现,也许更原始和通用的谓词是call_nth/2。
    • 我没有意识到可以这样调用目标(call_nth/2 列表的第三行)。我以为总是需要call(Goal),但显然,只需Goal 就足够了!
    【解决方案2】:

    如果您使用的是 SWI prolog,您可以使用 nb_getval/2nb_setval/2 来实现您想要的:

    lock_open:- 
      nb_setval(ctr, 0),  % Initialize counter
      conditional_combination(X), 
      nb_inc(ctr),  % Increment Counter
      equal(X,[8,6,5,3,6,9]),
      % Here you can access counter value with nb_getval(ctr, Value)
      !, 
      print(X).
    
    nb_inc(Key):-
      nb_getval(Key, Old),
      succ(Old, New),
      nb_setval(Key, New).
    

    其他 prolog 有其他方法可以做同样的事情,在你的 prolog 实现中寻找全局变量。在这个 sn-p 中,我使用术语 ctr 来保存当前目标计数器。您可以使用程序中未使用的任何术语。

    【讨论】:

    • conditional_combination/1 内调用nb_setval/2 会影响结果。 ctr 之类的名称可能用于其他计数...
    • @false: true,OP 应该使用他的程序中没有使用的任何术语。
    • 重点是,如果不检查整个程序,您无法保证这一点。
    • 你是对的,但如果你采取那个立场,那么你永远不会使用 assert/call/retract 或者实际上任何具有全局效果的东西。事实上,就像任何语言一样,如果你使用任何全局语言,你必须小心......
    • 我不同意:更新知识库需要诸如 assert/retract 之类的东西。并且有一些方法可以防止可能多次使用全局变量。以findall/3 使用 asserta/retract 为例。你不能嵌套这样的调用,也不能同时有两个调用。
    【解决方案3】:

    在开发“微型”模块时,我最近发明了枢轴。它们受到线程/管道模式的启发来传递数据。枢轴是最大长度为 1 的有界队列,pivot_put/1 也会复制给定的术语。但出于性能原因,它们不使用同步并且是非阻塞的。

    到目前为止,它们与 nb_setarg/3 非常相似,只是它们不破坏 Prolog 术语,而是更新 Java 数据结构。因此,它们比非逻辑项操作更安全一些。他们也不需要一些 call_cleanup/3,因为它们是 Java 垃圾收集的。

    到目前为止,它们比 nb_setarg/3 更相似,而不是使用一些显式分配和释放结构。例如,SICStus Prolog 的解决方案可能是:

    call_nth(Goal_0, Nth) :-
       new(unsigned_32, Counter),
       call_cleanup(call_nth1(Goal_0, Counter, Nth),
               dispose(Counter)).
    
    call_nth1(Goal_0, Counter, Nth) :-
       call(Goal_0),
       get_contents(Counter, contents, Count0),
       Count1 is Count0+1,
       put_contents(Counter, contents, Count1),
       Nth = Count1.
    

    有了pivot,甚至没有32位的限制,我们可以直接做:

    call_nth(G, C) :-
       pivot_new(P),
       pivot_put(P, 0),
       call(G),
       pivot_take(P, M),
       N is M+1,
       pivot_put(P, N),
       C = N.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-29
      • 2021-04-13
      • 1970-01-01
      • 2015-10-25
      • 1970-01-01
      • 1970-01-01
      • 2017-04-30
      相关资源
      最近更新 更多