count the number of calls of a clause
Asked Answered
F

3

13

I have a clause like following:

lock_open:-
        conditional_combination(X),
        equal(X,[8,6,5,3,6,9]),!,
        print(X).

this clause succeed. But I want to know how many times conditional_combination() is called before equal(X,[8,6,5,3,6,9]) is become true. the program is to generate a permutation by following some rules. And I need to how many permutation is need to generate to get a particular value like 865369.

Ferrite answered 6/7, 2012 at 14:55 Comment(0)
C
12

What you actually want is something slightly different: You want to count the number of answers (so far) of a goal.

The following predicate call_nth(Goal_0, Nth) succeeds like call(Goal_0) but has an additional argument which indicates that the answer found is the n-th answer. This definition is highly specific to SWI or YAP. Do not use things like nb_setarg/3 in your general programs, but use them for well encapsulated cases as this one. Even within those two systems, the precise meaning of these constructs is not well defined for the general case. Here is a definition for SICStus. Update: use unsigned_64 in newer versions instead of unsigned_32.

call_nth(Goal_0, Nth) :-
   nonvar(Nth),
   !,
   Nth \== 0,
   \+arg(Nth,+ 1,2), % produces all expected errors
   State = count(0,_), % note the extra argument which remains a variable
   Goal_0,
   arg(1, State, C1),
   C2 is C1+1,
   (  Nth == C2
   -> !
   ;  nb_setarg(1, State, C2),
      fail
   ).
call_nth(Goal_0, Nth) :-
   State = count(0,_), % note the extra argument which remains a variable
   Goal_0,
   arg(1, State, C1),
   C2 is C1+1,
   nb_setarg(1, State, C2),
   Nth = C2.

A more robust abstraction is provided by 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(between(1,5,I),Nth).
   I = Nth, Nth = 1
;  I = Nth, Nth = 2
;  I = Nth, Nth = 3
;  I = Nth, Nth = 4
;  I = Nth, Nth = 5.

So simply wrap it around:

lock_open :-
   call_nth(conditional_combination(X), Nth),
   X = [8,6,5,3,6,9],
   !,
   ....
Cretinism answered 9/7, 2012 at 17:41 Comment(3)
I see a way to do an aggregator O(N) in time and space using such primitive. Thanks!Diocese
Have to rethink the limit/2 and offset/2 implementation, maybe the more primitive and universal predicate would be call_nth/2.Cymry
I did not realize that a goal can be called like that (the third line of the call_nth/2 listing). I thought one always needs call(Goal), but obviously, just Goal is enough!Besot
C
4

If you are using SWI prolog you can use nb_getval/2 and nb_setval/2 to achieve what you want:

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).

Other prologs have other means to do the same, look for global variables in your prolog implementation. In this snippet I used the term ctr to hold the current goal counter. You can use any term there that is not used in your program.

Compulsion answered 6/7, 2012 at 17:30 Comment(5)
A call to nb_setval/2 within conditional_combination/1 would affect the result. A name like ctr might be used for some other counting...Cretinism
@false: true, OP should use any term not used in his program.Compulsion
The point was that you cannot guarantee that without inspecting your entire program.Cretinism
You are right, but if you take that position then you would never use assert/call/retract or in fact anything with global effect. In fact just, like in any language, if you use anything global you have to be cautious...Compulsion
I disagree: Updating a knowledge base needs something like assert/retract. And there are ways to prevent that multiple uses of a global are possible. Take as an example findall/3 using asserta/retract. You cannot nest such calls, nor can you have two calls at the same time.Cretinism
C
0

While working on a module "micro", I recently invented pivots. They are inspired by the thread / pipe pattern to pass around data. A pivot is a bounded queue of maximum length one, the pivot_put/1 does a copy of the given term as well. But for performance reasons they don't use a synchronized and are non-blocking.

In as far they are very similar to nb_setarg/3, except that they don't destruct a Prolog term, but instead they update a Java data structure. As a result they are little bit safer than the non-logical term operations. Also they don't need some call_cleanup/3, since they are Java garbage collected.

In as far they are more similar than nb_setarg/3, than using some explicit allocate and dealloccate of structures. So for example a solution for SICStus Prolog could be:

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.

With pivots, there is even no 32-bit limitation, and we can directly do:

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.
Cymry answered 5/3, 2019 at 16:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.