Add all the A tasks

This commit is contained in:
Ingy döt Net 2013-04-10 14:58:50 -07:00
parent 2dd7375f96
commit 051504d65b
1608 changed files with 18584 additions and 0 deletions

View file

@ -0,0 +1,17 @@
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 input is a function of time. 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''(''t'') and the output is ''S'', the object state ''S'' is changed to ''S'' + (''K''(''t''<sub>1</sub>) + ''K''(''t''<sub>0</sub>)) * (''t''<sub>1</sub> - ''t''<sub>0</sub>) / 2, i.e. it integrates ''K'' using the trapeze method. Initially ''K'' is constant 0 and ''S'' is 0.
In order to test the object:
# set its input to sin (2π ''f t''), where the frequency ''f''=0.5Hz. The phase is irrelevant.
# wait 2s
# set the input to constant 0
# wait 0.5s
Verify that now the object's output is approximately 0 (the sine has the period of 2s). The accuracy of the result will depend on the [[OS]] scheduler time slicing and the accuracy of the clock.
{{omit from|VBScript}}

View file

@ -0,0 +1,8 @@
---
category:
- Object oriented
note: Concurrency
requires:
- Concurrency
- Objects
- Mutable State

View file

@ -0,0 +1,65 @@
with Ada.Calendar; use Ada.Calendar;
with Ada.Numerics; use Ada.Numerics;
with Ada.Numerics.Elementary_Functions; use Ada.Numerics.Elementary_Functions;
with Ada.Text_IO; use Ada.Text_IO;
procedure Test_Integrator is
type Func is access function (T : Time) return Float;
function Zero (T : Time) return Float is
begin
return 0.0;
end Zero;
Epoch : constant Time := Clock;
function Sine (T : Time) return Float is
begin
return Sin (Pi * Float (T - Epoch));
end Sine;
task type Integrator is
entry Input (Value : Func);
entry Output (Value : out Float);
entry Shut_Down;
end Integrator;
task body Integrator is
K : Func := Zero'Access;
S : Float := 0.0;
F0 : Float := 0.0;
F1 : Float;
T0 : Time := Clock;
T1 : Time;
begin
loop
select
accept Input (Value : Func) do
K := Value;
end Input;
or accept Output (Value : out Float) do
Value := S;
end Output;
or accept Shut_Down;
exit;
else
T1 := Clock;
F1 := K (T1);
S := S + 0.5 * (F1 + F0) * Float (T1 - T0);
T0 := T1;
F0 := F1;
end select;
end loop;
end Integrator;
I : Integrator;
S : Float;
begin
I.Input (Sine'Access);
delay 2.0;
I.Input (Zero'Access);
delay 0.5;
I.Output (S);
Put_Line ("Integrated" & Float'Image (S) & "s");
I.Shut_Down;
end Test_Integrator;

View file

@ -0,0 +1,70 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <sys/time.h>
#include <pthread.h>
/* no need to lock the object: at worst the readout would be 1 tick off,
which is no worse than integrator's inate inaccuracy */
typedef struct {
double (*func)(double);
struct timeval start;
double v, last_v, last_t;
pthread_t id;
} integ_t, *integ;
void update(integ x)
{
struct timeval tv;
double t, v, (*f)(double);
f = x->func;
gettimeofday(&tv, 0);
t = ((tv.tv_sec - x->start.tv_sec) * 1000000
+ tv.tv_usec - x->start.tv_usec) * 1e-6;
v = f ? f(t) : 0;
x->v += (x->last_v + v) * (t - x->last_t) / 2;
x->last_t = t;
}
void* tick(void *a)
{
integ x = a;
while (1) {
usleep(100000); /* update every .1 sec */
update(x);
}
}
void set_input(integ x, double (*func)(double))
{
update(x);
x->func = func;
x->last_t = 0;
x->last_v = func ? func(0) : 0;
}
integ new_integ(double (*func)(double))
{
integ x = malloc(sizeof(integ_t));
x->v = x->last_v = 0;
x->func = 0;
gettimeofday(&x->start, 0);
set_input(x, func);
pthread_create(&x->id, 0, tick, x);
return x;
}
double sine(double t) { return sin(4 * atan2(1, 1) * t); }
int main()
{
integ x = new_integ(sine);
sleep(2);
set_input(x, 0);
usleep(500000);
printf("%g\n", x->v);
return 0;
}

View file

@ -0,0 +1,36 @@
(ns active-object
(:import (java.util Timer TimerTask)))
(defn input [integrator k]
(send integrator assoc :k k))
(defn output [integrator]
(:s @integrator))
(defn tick [integrator t1]
(send integrator
(fn [{:keys [k s t0] :as m}]
(assoc m :s (+ s (/ (* (+ (k t1) (k t0)) (- t1 t0)) 2.0)) :t0 t1))))
(defn start-timer [integrator interval]
(let [timer (Timer. true)
start (System/currentTimeMillis)]
(.scheduleAtFixedRate timer
(proxy [TimerTask] []
(run [] (tick integrator (double (/ (- (System/currentTimeMillis) start) 1000)))))
(long 0)
(long interval))
#(.cancel timer)))
(defn test-integrator []
(let [integrator (agent {:k (constantly 0.0) :s 0.0 :t0 0.0})
stop-timer (start-timer integrator 10)]
(input integrator #(Math/sin (* 2.0 Math/PI 0.5 %)))
(Thread/sleep 2000)
(input integrator (constantly 0.0))
(Thread/sleep 500)
(println (output integrator))
(stop-timer)))
user> (test-integrator)
1.414065859052494E-5

View file

@ -0,0 +1,80 @@
package main
import (
"fmt"
"math"
"time"
)
// type for input function, k.
// input is duration since an arbitrary start time t0.
type tFunc func(time.Duration) float64
// active integrator object. state variables are not here, but in
// function aif, started as a goroutine in the constructor.
type aio struct {
iCh chan tFunc // channel for setting input function
oCh chan chan float64 // channel for requesting output
}
// constructor
func newAio() *aio {
var a aio
a.iCh = make(chan tFunc)
a.oCh = make(chan chan float64)
go aif(&a)
return &a
}
// input method required by task description. in practice, this method is
// unnecessary; you would just put that single channel send statement in
// your code wherever you wanted to set the input function.
func (a aio) input(f tFunc) {
a.iCh <- f
}
// output method required by task description. in practice, this method too
// would not likely be best. instead any client interested in the value would
// likely make a return channel sCh once, and then reuse it as needed.
func (a aio) output() float64 {
sCh := make(chan float64)
a.oCh <- sCh
return <-sCh
}
// integration function that returns constant 0
func zeroFunc(time.Duration) float64 { return 0 }
// goroutine serializes access to integrated function k and state variable s
func aif(a *aio) {
var k tFunc = zeroFunc // integration function
s := 0. // "object state" initialized to 0
t0 := time.Now() // initial time
k0 := k(0) // initial sample value
t1 := t0 // t1, k1 used for trapezoid formula
k1 := k0
tk := time.Tick(10 * time.Millisecond) // 10 ms -> 100 Hz
for {
select {
case t2 := <-tk: // timer tick event
k2 := k(t2.Sub(t0)) // new sample value
s += (k1 + k2) * .5 * t2.Sub(t1).Seconds() // trapezoid formula
t1, k1 = t2, k2 // save time and value
case k = <-a.iCh: // input method event: function change
case sCh := <-a.oCh: // output method event: sample object state
sCh <- s
}
}
}
func main() {
a := newAio() // create object
a.input(func(t time.Duration) float64 { // 1. set input to sin function
return math.Sin(t.Seconds() * math.Pi)
})
time.Sleep(2 * time.Second) // 2. sleep 2 sec
a.input(zeroFunc) // 3. set input to zero function
time.Sleep(time.Second / 2) // 4. sleep .5 sec
fmt.Println(a.output()) // output should be near zero
}

View file

@ -0,0 +1,77 @@
module Integrator (
newIntegrator, input, output, stop,
Time, timeInterval
) where
import Control.Concurrent (forkIO, threadDelay)
import Control.Concurrent.MVar (MVar, newMVar, modifyMVar_, modifyMVar, readMVar)
import Control.Exception (evaluate)
import Data.Time (UTCTime)
import Data.Time.Clock (getCurrentTime, diffUTCTime)
-- RC task
main = do let f = 0.5 {- Hz -}
t0 <- getCurrentTime
i <- newIntegrator
input i (\t -> sin(2*pi * f * timeInterval t0 t)) -- task step 1
threadDelay 2000000 {- µs -} -- task step 2
input i (const 0) -- task step 3
threadDelay 500000 {- µs -} -- task step 4
result <- output i
stop i
print result
---- Implementation ------------------------------------------------------
-- Utilities for working with the time type
type Time = UTCTime
type Func a = Time -> a
timeInterval t0 t1 = realToFrac $ diffUTCTime t1 t0
-- Type signatures of the module's interface
newIntegrator :: Fractional a => IO (Integrator a) -- Create an integrator
input :: Integrator a -> Func a -> IO () -- Set the input function
output :: Integrator a -> IO a -- Get the current value
stop :: Integrator a -> IO () -- Stop integration, don't waste CPU
-- Data structures
data Integrator a = Integrator (MVar (IntState a)) -- MVar is a thread-safe mutable cell
deriving Eq
data IntState a = IntState { func :: Func a, -- The current function
run :: Bool, -- Whether to keep going
value :: a, -- The current accumulated value
time :: Time } -- The time of the previous update
newIntegrator = do
now <- getCurrentTime
state <- newMVar $ IntState { func = const 0,
run = True,
value = 0,
time = now }
thread <- forkIO (intThread state) -- The state variable is shared between the thread
return (Integrator state) -- and the client interface object.
input (Integrator stv) f = modifyMVar_ stv (\st -> return st { func = f })
output (Integrator stv) = fmap value $ readMVar stv
stop (Integrator stv) = modifyMVar_ stv (\st -> return st { run = False })
-- modifyMVar_ takes an MVar and replaces its contents according to the provided function.
-- a { b = c } is record-update syntax: "the record a, except with field b changed to c"
-- Integration thread
intThread :: Fractional a => MVar (IntState a) -> IO ()
intThread stv = whileM $ modifyMVar stv updateAndCheckRun
-- modifyMVar is like modifyMVar_ but the function returns a tuple of the new value
-- and an arbitrary extra value, which in this case ends up telling whileM whether
-- to keep looping.
where updateAndCheckRun st = do
now <- getCurrentTime
let value' = integrate (func st) (value st) (time st) now
evaluate value' -- avoid undesired laziness
return (st { value = value', time = now }, -- updated state
run st) -- whether to continue
integrate :: Fractional a => Func a -> a -> Time -> Time -> a
integrate f value t0 t1 = value + (f t0 + f t1)/2 * dt
where dt = timeInterval t0 t1
-- Execute 'action' until it returns false.
whileM action = do b <- action; if b then whileM action else return ()

View file

@ -0,0 +1,26 @@
function Integrator(sampleIntervalMS) {
var inputF = function () { return 0.0 };
var sum = 0.0;
var t1 = new Date().getTime();
var input1 = inputF(t1 / 1000);
function update() {
var t2 = new Date().getTime();
var input2 = inputF(t2 / 1000);
var dt = (t2 - t1) / 1000;
sum += (input1 + input2) * dt / 2;
t1 = t2;
input1 = input2;
}
var updater = setInterval(update, sampleIntervalMS);
return ({
input: function (newF) { inputF = newF },
output: function () { return sum },
shutdown: function () { clearInterval(updater) },
});
}

View file

@ -0,0 +1,20 @@
<p><span id="a">Test running...</span> <code id="b">-</code></p>
<script type="text/javascript">
var f = 0.5;
var i = new Integrator(1);
var displayer = setInterval(function () { document.getElementById("b").firstChild.data = i.output() }, 100)
setTimeout(function () {
i.input(function (t) { return Math.sin(2*Math.PI*f*t) }); // test step 1
setTimeout(function () { // test step 2
i.input(function (t) { return 0 }); // test step 3
setTimeout(function () { // test step 3
i.shutdown();
clearInterval(displayer);
document.getElementById("a").firstChild.data = "Done, should be about 0: "
}, 500);
}, 2000);
}, 1)
</script>

View file

@ -0,0 +1,69 @@
#!/usr/bin/perl
use strict;
use 5.10.0;
package Integrator;
use threads;
use threads::shared;
sub new {
my $cls = shift;
my $obj = bless { t => 0,
sum => 0,
ref $cls ? %$cls : (),
stop => 0,
tid => 0,
func => shift,
}, ref $cls || $cls;
share($obj->{sum});
share($obj->{stop});
$obj->{tid} = async {
my $upd = 0.1; # update every 0.1 second
while (!$obj->{stop}) {
{
my $f = $obj->{func};
my $t = $obj->{t};
$obj->{sum} += ($f->($t) + $f->($t + $upd))* $upd/ 2;
$obj->{t} += $upd;
}
select(undef, undef, undef, $upd);
}
# say "stopping $obj";
};
$obj
}
sub output { shift->{sum} }
sub delete {
my $obj = shift;
$obj->{stop} = 1;
$obj->{tid}->join;
}
sub setinput {
# This is surprisingly difficult because of the perl sharing model.
# Func refs can't be shared, thus can't be replaced by another thread.
# Have to create a whole new object... there must be a better way.
my $obj = shift;
$obj->delete;
$obj->new(shift);
}
package main;
my $x = Integrator->new(sub { sin(atan2(1, 1) * 8 * .5 * shift) });
sleep(2);
say "sin after 2 seconds: ", $x->output;
$x = $x->setinput(sub {0});
select(undef, undef, undef, .5);
say "0 after .5 seconds: ", $x->output;
$x->delete;

View file

@ -0,0 +1,44 @@
(load "@lib/math.l")
(class +Active)
# inp val sum usec
(dm T ()
(unless (assoc -100 *Run) # Install timer task
(task -100 100 # Update objects every 0.1 sec
(mapc 'update> *Actives) ) )
(=: inp '((U) 0)) # Set zero input function
(=: val 0) # Initialize last value
(=: sum 0) # Initialize sum
(=: usec (usec)) # and time
(push '*Actives This) ) # Install in notification list
(dm input> (Fun)
(=: inp Fun) )
(dm update> ()
(let (U (usec) V ((: inp) U)) # Get current time, calculate value
(inc (:: sum)
(*/
(+ V (: val)) # (K(t[1]) + K(t[0])) *
(- U (: usec)) # (t[1] - t[0]) /
2.0 ) ) # 2.0
(=: val V)
(=: usec U) ) )
(dm output> ()
(format (: sum) *Scl) ) # Get result
(dm stop> ()
(unless (del This '*Actives) # Removing the last active object?
(task -100) ) ) # Yes: Uninstall timer task
(de integrate () # Test it
(let Obj (new '(+Active)) # Create an active object
(input> Obj # Set input function
'((U) (sin (*/ pi U 1.0))) ) # to sin(π * t)
(wait 2000) # Wait 2 sec
(input> Obj '((U) 0)) # Reset input function
(wait 500) # Wait 0.5 sec
(prinl "Output: " (output> Obj)) # Print return value
(stop> Obj) ) ) # Stop active object

View file

@ -0,0 +1,38 @@
from time import time, sleep
from threading import Thread
class Integrator(Thread):
'continuously integrate a function `K`, at each `interval` seconds'
def __init__(self, K=lambda t:0, interval=1e-4):
Thread.__init__(self)
self.interval = interval
self.K = K
self.S = 0.0
self.__run = True
self.start()
def run(self):
"entry point for the thread"
interval = self.interval
start = time()
t0, k0 = 0, self.K(0)
while self.__run:
sleep(interval)
t1 = time() - start
k1 = self.K(t1)
self.S += (k1 + k0)*(t1 - t0)/2.0
t0, k0 = t1, k1
def join(self):
self.__run = False
Thread.join(self)
if __name__ == "__main__":
from math import sin, pi
ai = Integrator(lambda t: sin(pi*t))
sleep(2)
print ai.S
ai.K = lambda t: 0
sleep(0.5)
print ai.S

View file

@ -0,0 +1,30 @@
#lang racket
(require (only-in racket/gui sleep/yield timer%))
(define active%
(class object%
(super-new)
(init-field k) ; input function
(field [s 0]) ; state
(define t_0 0)
(define/public (input new-k) (set! k new-k))
(define/public (output) s)
(define (callback)
(define t_1 (/ (- (current-inexact-milliseconds) start) 1000))
(set! s (+ s (* (+ (k t_0) (k t_1))
(/ (- t_1 t_0) 2))))
(set! t_0 t_1))
(define start (current-inexact-milliseconds))
(new timer%
[interval 1000]
[notify-callback callback])))
(define active (new active% [k (λ (t) (sin (* 2 pi 0.5 t)))]))
(sleep/yield 2)
(send active input (λ _ 0))
(sleep/yield 0.5)
(displayln (send active output))

View file

@ -0,0 +1,48 @@
object ActiveObject {
class Integrator {
import java.util._
import scala.actors.Actor._
case class Pulse(t: Double)
case class Input(k: Double => Double)
case object Output
case object Bye
val timer = new Timer(true)
var k: Double => Double = (_ => 0.0)
var s: Double = 0.0
var t0: Double = 0.0
val handler = actor {
loop {
react {
case Pulse(t1) => s += (k(t1) + k(t0)) * (t1 - t0) / 2.0; t0 = t1
case Input(k) => this.k = k
case Output => reply(s)
case Bye => timer.cancel; exit
}
}
}
timer.scheduleAtFixedRate(new TimerTask {
val start = System.currentTimeMillis
def run { handler ! Pulse((System.currentTimeMillis - start) / 1000.0) }
}, 0, 10) // send Pulse every 10 ms
def input(k: Double => Double) = handler ! Input(k)
def output = handler !? Output
def bye = handler ! Bye
}
def main(args: Array[String]) {
val integrator = new Integrator
integrator.input(t => Math.sin(2.0 * Math.Pi * 0.5 * t))
Thread.sleep(2000)
integrator.input(_ => 0.0)
Thread.sleep(500)
println(integrator.output)
integrator.bye
}
}

View file

@ -0,0 +1,50 @@
package require Tcl 8.6
oo::class create integrator {
variable e sum delay tBase t0 k0 aid
constructor {{interval 1}} {
set delay $interval
set tBase [clock microseconds]
set t0 0
set e { 0.0 }
set k0 0.0
set sum 0.0
set aid [after $delay [namespace code {my Step}]]
}
destructor {
after cancel $aid
}
method input expression {
set e $expression
}
method output {} {
return $sum
}
method Eval t {
expr $e
}
method Step {} {
set aid [after $delay [namespace code {my Step}]]
set t [expr {([clock microseconds] - $tBase) / 1e6}]
set k1 [my Eval $t]
set sum [expr {$sum + ($k1 + $k0) * ($t - $t0) / 2.}]
set t0 $t
set k0 $k1
}
}
set pi 3.14159265
proc pause {time} {
yield [after [expr {int($time * 1000)}] [info coroutine]]
}
proc task {script} {
coroutine task_ apply [list {} "$script;set ::done ok"]
vwait done
}
task {
integrator create i
i input {sin(2*$::pi * 0.5 * $t)}
pause 2
i input { 0.0 }
pause 0.5
puts [format %.15f [i output]]
}