How to accumulate in a repeat (forever)

Viewed 64

In a context of command and control of a hardware device, I need an infinite loop "acquire-elaborate-publish" with a memorization of the current state, to observe evolution of inputs like a boolean which "becomes" true.

I have coded a mockup, the following program, which produce a strange behaviour (for me) and I'm surprised because I set the bounded variable Previous without failure, why?

I expect an error message like 'variable Previous is already bound' but no, backtracking is done to resolve constraints (many times!).

#!/usr/bin/swipl

init( Previous, AtStart ) :-
    get_time( AtStart ),
    Previous is 0.

run( AtStart, Previous ) :- % I want this to be executed only once for each period!
    get_time( Now ),
    Elapsed is Now - AtStart,
    Current is Previous + random( 20 ),
    format( "~w: Previous = ~w~n", [Elapsed, Previous] ),
    format( "~w: Current  = ~w~n", [Elapsed, Current ] ),
    Previous is Current.

periodicTask :-
    init( Previous, AtStart ),
    repeat,
        run( AtStart, Previous ),
        sleep( 1.0 ).

:-
    periodicTask.

It runs indefinitely with this kind of output:

?- periodicTask.
?- periodicTask.
0.0231266: Previous = 0
0.0231266: Current  = 10
0.243902: Previous = 0
0.243902: Current  = 16
............................ A lot of lines
0.934601: Previous = 0
0.934601: Current  = 0
true ;
2693.8: Previous = 0
2693.8: Current  = 19
............................ A lot of lines
2694.65: Previous = 0
2694.65: Current  = 0
 true ;
3694.98: Previous = 0
3694.98: Current  = 2
............................ A lot of lines
3695.17: Previous = 0
3695.17: Current  = 0
 true ;
4695.47: Previous = 0
4695.47: Current  = 10
............................ A lot of lines
4695.55: Previous = 0
4695.55: Current  = 0
true 
  1. Why?

  2. How to code an infinite loop which reset (unbound) some variables and preserve a global context?

The answer 'by recursion' does not seem applicable here, no?

2 Answers

Previous is unified with the value 0 in init/2. So when Previous is "recalculated" in run/2, and can be unified with the value 0, all goes well. Here is a working version of your code, using a dynamic predicate counter/1 to keep track of the number of iterations.

?- dynamic counter/1.

init(AtStart ) :-
    get_time( AtStart ),
    retractall(counter(_)), % clear all counter
    asserta(counter(0)).    % set it with initial value

run( AtStart, Iteration ) :- % I want this to be executed only once for each period!
    get_time( Now ),
    Elapsed is Now - AtStart,
    format( "~w: Iteration = ~w~n", [Elapsed, Iteration]).

periodicTask :-
    init(AtStart),
    repeat,
        counter(N),        %scope of counter is inside repeat/0

        run( AtStart, N),  % Your code

        N1 is N+1,
        retract(counter(N)), 
        asserta(counter(N1)), %update counter
        sleep( 1.0 ).

Here is execution:

?- periodicTask.
0.0: Iteration = 0
true ;
1.7344958782196045: Iteration = 1
true ;
2.7745978832244873: Iteration = 2
true ;
4.572326898574829: Iteration = 3
true .

Does this anwser your question ?

Here is a simple loop written with a tail-recursive call which transmits the "state" at activation T over to the "state" at activation T+1, quite naturally (as Gödel intended before everybody wussed out and started to use for and while):

periodicTask :-
    get_time(AtStart),
    run(AtStart,0).        

stopCriteriumFulfilled :- fail. % TO BE DONE

run(AtStart,Previous) :-
    !,                       % Possibly optional: drop open chociepoints in infinite loop                       
    get_time(Now),
    Elapsed is Now - AtStart,
    Current is Previous + random( 20 ),
    format( "~w: Previous = ~w~n", [Elapsed, Previous] ),
    format( "~w: Current  = ~w~n", [Elapsed, Current ] ),
    % 
    % Now call yourself with a new context in which the 
    % variable "Current" ("here", in this activation)
    % becomes the variable "Previous" ("there", in the next activation)
    % But first sleep a bit (N.B. numbers are numbers, one can use 1 
    % instead of 1.0)
    % One may want to add a stop criterium here to succeed the predicate
    % instead of performing the tail-recursive call unconditionally.
    % 
    (stopCriteriumFulfilled
     -> true
      ; (sleep(1),
         run(AtStart,Current))). % RECURSIVE TAIL CALL
 
:- periodicTask.

If more "statefulness" is required than passing the current state to the next activation, take a look of these:

Related