Active object: Difference between revisions

From Rosetta Code
Content added Content deleted
(add not quite finished E example)
(fix E example, meet intent of task, add note)
Line 75: Line 75:
</pre>
</pre>


{{header|E}}
=={{header|E}}==

This example is not quite debugged yet; the author is going to fix it shortly.


def makeIntegrator() {
def makeIntegrator() {
Line 83: Line 81:
var dr := 0.0
var dr := 0.0
var t := timer.now()
var t := timer.now()
def update() {
def update() {
def t2 := timer.now()
def t2 := timer.now()
r += dr * (t2 - t)
r += dr * (t2 - t) / 1000
t := t2
t := t2
}
}
var task() {
update()
task <- ()
}
task()
def integrator {
def integrator {
to input(new :float64) :void {
to input(new :float64) :void {
update()
dr := new
dr := new
}
}
to output() :float64 {
to output() :float64 {
update()
return r
return r
}
to shutdown() {
task := fn {}
}
}
}
}
Line 107: Line 112:
def base := timer.now()
def base := timer.now()
def i := makeIntegrator()
def i := makeIntegrator()
i.input(2)
i.input(1)
timer.whenPast(base + 2000, fn {
timer.whenPast(base + 2000, fn {
i.input(0)
i.input(0)
Line 117: Line 122:
i.input(0)
i.input(0)
bind result := i.output()
bind result := i.output()
i.shutdown()
})
})
return result
return result
}
}

This integrator need not actually use any continuous background processing; it can compute only when interacted with. To do this, insert <code>update()</code> at the beginning of the input and output methods, and delete the definitions of <code>task</code> and <code>shutdown</code>.

Revision as of 01:29, 4 November 2008

Task
Active object
You are encouraged to solve this task according to the task description, using any language you may know.

In object-oriented programming an object is active when its state depends on clock. Usually an active object encapsulates a task that updates the object's state. To the outer world the object looks like a normal object with methods that can be called from outside. Implementation of such methods must have a certain synchronization mechanism with the encapsulated task in order to prevent object's state corruption.

A typical instance of an active object is an animation widget. The widget state changes with the time, while as an object it has all properties of a normal widget.

The task

Implement an active integrator object. The object has an input and output. The input can be set using the method Input. The output can be queried using the method Output. The object integrates its input over the time and the result becomes the object's output. So if the input is K and the output is S, the object state S is changed to S + K * dT, where dT is the time increment. Initially K and S are 0.

In order to test the object:

  1. set its input to 1
  2. wait 2s
  3. set the input to 0
  4. wait 0.5s
  5. set it to 0.5
  6. wait 1s
  7. set it to 0.

Verify that now the object's output is approximately 2.5s. The accuracy of the result will depend on the OS scheduler time slicing and the accuracy of the clock.

Note, float numbers were intentionally chosen in order to prevent use of built-in atomic operations supported by modern processors (like integer atomic increment, for instance). The objective of the task is to illustrate interplay of the object's the public methods and the encapsulated task, rather than some hardware tricks.

Ada

<ada> with Ada.Calendar; use Ada.Calendar; with Ada.Text_IO; use Ada.Text_IO;

procedure Test_Integrator is

  task type Integrator is
     entry Input  (Value : Float);
     entry Output (Value : out Float);
     entry Shut_Down;
  end Integrator;
  task body Integrator is
     K, S : Float := 0.0;
     T0 : Time := Clock;
     T1 : Time;
  begin
     loop
        select
           accept Input (Value : Float) do
              K := Value;
           end Input;
        or accept Output (Value : out Float) do
              Value := S;
           end Output;
        or accept Shut_Down;
           exit;
        else
           T1 := Clock;
           S := S + K * Float (T1 - T0);
           T0 := T1;
        end select;
     end loop;
  end Integrator;
  I : Integrator;
  S : Float;

begin

  I.Input (1.0);
  delay 2.0;
  I.Input (0.0);
  delay 0.5;
  I.Input (0.5);
  delay 1.0;
  I.Input (0.0);
  I.Output (S);
  Put_Line ("Integrated" & Float'Image (S) & "s");
  I.Shut_Down;

end Test_Integrator; </ada> Sample output:

Integrated 2.60153E+00s

E

def makeIntegrator() {
    var r := 0.0
    var dr := 0.0
    var t := timer.now()
    
    def update() {
        def t2 := timer.now()
        r += dr * (t2 - t) / 1000
        t := t2
    }
    
    var task() {
      update()
      task <- ()
    }
    task()
    
    def integrator {
        to input(new :float64) :void {
            dr := new
        }
        to output() :float64 {
            return r
        }
        to shutdown() {
            task := fn {}
        }
    }
    return integrator
}
def test() {
    def result
    def base := timer.now()
    def i := makeIntegrator()
    i.input(1)
    timer.whenPast(base + 2000, fn {
        i.input(0)
    })
    timer.whenPast(base + 2500, fn {
        i.input(0.5)
    })
    timer.whenPast(base + 3500, fn {
        i.input(0)
        bind result := i.output()
        i.shutdown()
    })
    return result
}

This integrator need not actually use any continuous background processing; it can compute only when interacted with. To do this, insert update() at the beginning of the input and output methods, and delete the definitions of task and shutdown.