Active object: Difference between revisions
Content added Content deleted
Thundergnat (talk | contribs) m (→{{header|Perl 6}}: avoid a race condition) |
Thundergnat (talk | contribs) m (alphabetize) |
||
Line 44: | Line 44: | ||
entry Output (Value : out Float); |
entry Output (Value : out Float); |
||
entry Shut_Down; |
entry Shut_Down; |
||
end Integrator; |
end Integrator;A072954 |
||
task body Integrator is |
task body Integrator is |
||
Line 196: | Line 196: | ||
output |
output |
||
<pre>-9.99348e-05</pre> |
<pre>-9.99348e-05</pre> |
||
A072954 |
|||
=={{header|C#}}== |
=={{header|C#}}== |
||
{{works with|C# 6}} |
{{works with|C# 6}} |
||
Line 1,260: | Line 1,260: | ||
v2 = it.runningsum |
v2 = it.runningsum |
||
println("After 2.5 seconds, integrator value was $v2")</lang> |
println("After 2.5 seconds, integrator value was $v2")</lang> |
||
=={{header|Lingo}}== |
|||
Parent script "Integrator": |
|||
<lang Lingo>property _sum |
|||
property _func |
|||
property _timeLast |
|||
property _valueLast |
|||
property _ms0 |
|||
property _updateTimer |
|||
on new (me, func) |
|||
if voidP(func) then func = "0.0" |
|||
me._sum = 0.0 |
|||
-- update frequency: 100/sec (arbitrary) |
|||
me._updateTimer = timeout().new("update", 10, #_update, me) |
|||
me.input(func) |
|||
return me |
|||
end |
|||
on stop (me) |
|||
me._updateTimer.period = 0 -- deactivates timer |
|||
end |
|||
-- func is a term (as string) that might contain "t" and is evaluated at runtime |
|||
on input (me, func) |
|||
me._func = func |
|||
me._ms0 = _system.milliseconds |
|||
me._timeLast = 0.0 |
|||
t = 0.0 |
|||
me._valueLast = value(me._func) |
|||
end |
|||
on output (me) |
|||
return me._sum |
|||
end |
|||
on _update (me) |
|||
now = _system.milliseconds - me._ms0 |
|||
t = now/1000.0 |
|||
val = value(me._func) |
|||
me._sum = me._sum + (me._valueLast+val)*(t - me._timeLast)/2 |
|||
me._timeLast = t |
|||
me._valueLast = val |
|||
end</lang> |
|||
In some movie script: |
|||
<lang Lingo>global gIntegrator |
|||
-- entry point |
|||
on startMovie |
|||
gIntegrator = script("Integrator").new("sin(PI * t)") |
|||
timeout().new("timer", 2000, #step1) |
|||
end |
|||
on step1 (_, timer) |
|||
gIntegrator.input("0.0") |
|||
timer.timeoutHandler = #step2 |
|||
timer.period = 500 |
|||
end |
|||
on step2 (_, timer) |
|||
gIntegrator.stop() |
|||
put gIntegrator.output() |
|||
timer.forget() |
|||
end</lang> |
|||
{{out}} |
|||
<pre>-- 0.0004</pre> |
|||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
||
Line 1,407: | Line 1,339: | ||
</pre> |
</pre> |
||
=={{header| |
=={{header|Lingo}}== |
||
Parent script "Integrator": |
|||
<lang Mathematica>Block[{start = SessionTime[], K, t0 = 0, t1, kt0, S = 0}, |
|||
<lang Lingo>property _sum |
|||
K[t_] = Sin[2 Pi f t] /. f -> 0.5; kt0 = K[t0]; |
|||
property _func |
|||
While[True, t1 = SessionTime[] - start; |
|||
property _timeLast |
|||
S += (kt0 + (kt0 = K[t1])) (t1 - t0)/2; t0 = t1; |
|||
property _valueLast |
|||
If[t1 > 2, K[t_] = 0; If[t1 > 2.5, Break[]]]]; S]</lang> |
|||
property _ms0 |
|||
property _updateTimer |
|||
on new (me, func) |
|||
1.1309*10^-6 |
|||
if voidP(func) then func = "0.0" |
|||
me._sum = 0.0 |
|||
-- update frequency: 100/sec (arbitrary) |
|||
me._updateTimer = timeout().new("update", 10, #_update, me) |
|||
me.input(func) |
|||
return me |
|||
end |
|||
on stop (me) |
|||
Curiously, this value never changes; it is always exactly the same (at 1.1309E-6). Note that closer answers could be achieved by using Mathematica's better interpolation methods, but it would require collecting the data (in a list), which would have a speed penalty large enough to negate the improved estimation. |
|||
me._updateTimer.period = 0 -- deactivates timer |
|||
end |
|||
-- func is a term (as string) that might contain "t" and is evaluated at runtime |
|||
=={{header|Oz}}== |
|||
on input (me, func) |
|||
<lang oz>declare |
|||
me._func = func |
|||
fun {Const X} |
|||
me._ms0 = _system.milliseconds |
|||
fun {$ _} X end |
|||
me._timeLast = 0.0 |
|||
end |
|||
t = 0.0 |
|||
me._valueLast = value(me._func) |
|||
end |
|||
on output (me) |
|||
fun {Now} |
|||
return me._sum |
|||
{Int.toFloat {Property.get 'time.total'}} / 1000.0 |
|||
end |
|||
on _update (me) |
|||
class Integrator from Time.repeat |
|||
now = _system.milliseconds - me._ms0 |
|||
attr |
|||
t = now/1000.0 |
|||
val = value(me._func) |
|||
me._sum = me._sum + (me._valueLast+val)*(t - me._timeLast)/2 |
|||
t1 k_t1 |
|||
me._timeLast = t |
|||
me._valueLast = val |
|||
end</lang> |
|||
meth init(SampleIntervalMS) |
|||
t1 := {Now} |
|||
k_t1 := {@k @t1} |
|||
{self setRepAll(action:Update |
|||
delay:SampleIntervalMS)} |
|||
thread |
|||
{self go} |
|||
end |
|||
end |
|||
In some movie script: |
|||
meth input(K) |
|||
<lang Lingo>global gIntegrator |
|||
k := K |
|||
end |
|||
-- entry point |
|||
meth output($) |
|||
on startMovie |
|||
@s |
|||
gIntegrator = script("Integrator").new("sin(PI * t)") |
|||
end |
|||
timeout().new("timer", 2000, #step1) |
|||
end |
|||
on step1 (_, timer) |
|||
meth Update |
|||
gIntegrator.input("0.0") |
|||
t2 := {Now} |
|||
timer.timeoutHandler = #step2 |
|||
k_t2 := {@k @t2} |
|||
timer.period = 500 |
|||
s := @s + (@k_t1 + @k_t2) * (@t2 - @t1) / 2.0 |
|||
end |
|||
t1 := @t2 |
|||
k_t1 := @k_t2 |
|||
end |
|||
end |
|||
on step2 (_, timer) |
|||
Pi = 3.14159265 |
|||
gIntegrator.stop() |
|||
put gIntegrator.output() |
|||
timer.forget() |
|||
end</lang> |
|||
{{out}} |
|||
I = {New Integrator init(10)} |
|||
<pre>-- 0.0004</pre> |
|||
in |
|||
{I input(fun {$ T} |
|||
{Sin 2.0 * Pi * F * T} |
|||
end)} |
|||
=={{header|Mathematica}}== |
|||
{Delay 2000} %% ms |
|||
<lang Mathematica>Block[{start = SessionTime[], K, t0 = 0, t1, kt0, S = 0}, |
|||
K[t_] = Sin[2 Pi f t] /. f -> 0.5; kt0 = K[t0]; |
|||
While[True, t1 = SessionTime[] - start; |
|||
S += (kt0 + (kt0 = K[t1])) (t1 - t0)/2; t0 = t1; |
|||
If[t1 > 2, K[t_] = 0; If[t1 > 2.5, Break[]]]]; S]</lang> |
|||
1.1309*10^-6 |
|||
{I input({Const 0.0})} |
|||
Curiously, this value never changes; it is always exactly the same (at 1.1309E-6). Note that closer answers could be achieved by using Mathematica's better interpolation methods, but it would require collecting the data (in a list), which would have a speed penalty large enough to negate the improved estimation. |
|||
{Delay 500} %% ms |
|||
{Show {I output($)}} |
|||
{I stop}</lang> |
|||
=={{header|ooRexx}}== |
=={{header|ooRexx}}== |
||
Line 1,554: | Line 1,492: | ||
::routine sine |
::routine sine |
||
use arg t |
use arg t=={{header|OxygenBasic}}== |
||
Built from scratch. The ringmaster orchestrates all the active-objects, keeping a list of |
|||
return rxcalcsin(rxcalcpi() * t) |
|||
each individual and its method call. |
|||
With a high precision timer the result is around -.0002 |
|||
::requires rxmath library |
|||
<lang oxygenbasic> |
|||
double MainTime |
|||
'=============== |
|||
</lang> |
|||
class RingMaster |
|||
'=============== |
|||
' |
|||
indexbase 1 |
|||
sys List[512] 'limit of 512 objects per ringmaster |
|||
sys max,acts |
|||
' |
|||
method Register(sys meth,obj) as sys |
|||
sys i |
|||
for i=1 to max step 2 |
|||
if list[i]=0 then exit for 'vacant slot |
|||
next |
|||
if i>=max then max+=2 |
|||
List[i]<=meth,obj |
|||
return i 'token for deregistration etc |
|||
end method |
|||
' |
|||
method Deregister(sys *i) |
|||
if i then List[i]<=0,0 : i=0 |
|||
end method |
|||
' |
|||
method Clear() |
|||
max=0 |
|||
end method |
|||
' |
|||
method Act() 'called by the timer |
|||
sys i,q |
|||
for i=1 to max step 2 |
|||
q=List[i] |
|||
if q then |
|||
call q List[i+1] 'anon object |
|||
end if |
|||
next |
|||
acts++ |
|||
end method |
|||
' |
|||
end class |
|||
=={{header|Perl}}== |
|||
<lang perl>#!/usr/bin/perl |
|||
'================= |
|||
use strict; |
|||
class ActiveObject |
|||
use 5.10.0; |
|||
'================= |
|||
' |
|||
double s,freq,t1,t2,v1,v2 |
|||
sys nfun,acts,RingToken |
|||
RingMaster *Master |
|||
' |
|||
method fun0() as double |
|||
end method |
|||
' |
|||
method fun1() as double |
|||
return sin(2*pi()*freq*MainTime) |
|||
end method |
|||
' |
|||
method func() as double |
|||
select case nfun |
|||
case 0 : return fun0() |
|||
case 1 : return fun1() |
|||
end select |
|||
'error? |
|||
end method |
|||
' |
|||
method TimeBasedDuties() |
|||
t1=t2 |
|||
v1=v2 |
|||
t2=MainTime |
|||
v2=func |
|||
s=s+(v2+v1)*(t2-t1)*0.5 'add slice to integral |
|||
acts++ |
|||
end method |
|||
' |
|||
method RegisterWith(RingMaster*r) |
|||
@Master=@r |
|||
if @Master then |
|||
RingToken=Master.register @TimeBasedDuties,@this |
|||
end if |
|||
end method |
|||
' |
|||
method Deregister() |
|||
if @Master then |
|||
Master.Deregister RingToken 'this is set to null |
|||
end if |
|||
end method |
|||
' |
|||
method Output() as double |
|||
return s |
|||
end method |
|||
' |
|||
method Input(double fr=0,fun=0) |
|||
if fr then freq=fr |
|||
nfun=fun |
|||
end method |
|||
method ClearIntegral() |
|||
package Integrator; |
|||
s=0 |
|||
use threads; |
|||
end method |
|||
use threads::shared; |
|||
' |
|||
end class |
|||
sub new { |
|||
my $cls = shift; |
|||
my $obj = bless { t => 0, |
|||
sum => 0, |
|||
ref $cls ? %$cls : (), |
|||
stop => 0, |
|||
tid => 0, |
|||
func => shift, |
|||
}, ref $cls || $cls; |
|||
'SETUP TIMING SYSTEM |
|||
share($obj->{sum}); |
|||
'=================== |
|||
share($obj->{stop}); |
|||
extern library "kernel32.dll" |
|||
$obj->{tid} = async { |
|||
declare QueryPerformanceCounter (quad*c) |
|||
my $upd = 0.1; # update every 0.1 second |
|||
declare QueryPerformanceFrequency(quad*f) |
|||
while (!$obj->{stop}) { |
|||
declare Sleep(sys milliseconds) |
|||
{ |
|||
end extern |
|||
my $f = $obj->{func}; |
|||
' |
|||
my $t = $obj->{t}; |
|||
quad scount,tcount,freq |
|||
QueryPerformanceFrequency freq |
|||
double tscale=1/freq |
|||
double t1,t2 |
|||
QueryPerformanceCounter scount |
|||
macro PrecisionTime(time) |
|||
$obj->{sum} += ($f->($t) + $f->($t + $upd))* $upd/ 2; |
|||
QueryPerformanceCounter tcount |
|||
$obj->{t} += $upd; |
|||
time=(tcount-scount)*tscale |
|||
} |
|||
end macro |
|||
select(undef, undef, undef, $upd); |
|||
} |
|||
# say "stopping $obj"; |
|||
}; |
|||
$obj |
|||
} |
|||
sub output { shift->{sum} } |
|||
'==== |
|||
sub delete { |
|||
'TEST |
|||
my $obj = shift; |
|||
'==== |
|||
$obj->{stop} = 1; |
|||
$obj->{tid}->join; |
|||
} |
|||
double integral |
|||
sub setinput { |
|||
double tevent1,tevent2 |
|||
# This is surprisingly difficult because of the perl sharing model. |
|||
RingMaster Rudolpho |
|||
# Func refs can't be shared, thus can't be replaced by another thread. |
|||
ActiveObject A |
|||
# Have to create a whole new object... there must be a better way. |
|||
' |
|||
my $obj = shift; |
|||
A.RegisterWith Rudolpho |
|||
$obj->delete; |
|||
A.input (fr=0.5, fun=1) 'start with the freqency function (1) |
|||
$obj->new(shift); |
|||
' |
|||
} |
|||
'SET EVENT TIMES |
|||
'=============== |
|||
tEvent1=2.0 'seconds |
|||
package main; |
|||
tEvent2=2.5 'seconds |
|||
' |
|||
PrecisionTime t1 'mark initial time |
|||
MainTime=t1 |
|||
' |
|||
' |
|||
'EVENT LOOP |
|||
'========== |
|||
' |
|||
do |
|||
PrecisionTime t2 |
|||
MainTime=t2 |
|||
if t2-t1>=0.020 'seconds interval |
|||
Rudolpho.Act 'service all active objects |
|||
t1=t2 |
|||
end if |
|||
' |
|||
if tEvent1>=0 and MainTime>=tEvent1 |
|||
A.input (fun=0) 'switch to null function (0) |
|||
tEvent1=-1 'disable this event from happening again |
|||
end if |
|||
if MainTime>=tEvent2 |
|||
integral=A.output() |
|||
exit do 'end of session |
|||
end if |
|||
' |
|||
sleep 5 'hand control to OS for a while |
|||
end doA072954 |
|||
print str(integral,4) |
|||
my $x = Integrator->new(sub { sin(atan2(1, 1) * 8 * .5 * shift) }); |
|||
Rudolpho.clear</lang> |
|||
sleep(2); |
|||
return rxcalcsin(rxcalcpi() * t) |
|||
say "sin after 2 seconds: ", $x->output; |
|||
::requires rxmath library |
|||
$x = $x->setinput(sub {0}); |
|||
</lang> |
|||
select(undef, undef, undef, .5); |
|||
say "0 after .5 seconds: ", $x->output; |
|||
$x->delete;</lang> |
|||
=={{header|Perl 6}}== |
|||
{{works with|Rakudo|2018.12}} |
|||
There is some jitter in the timer, but it is typically accurate to within a few thousandths of a second. |
|||
<lang perl6>class Integrator { |
|||
has $.f is rw = sub ($t) { 0 }; |
|||
has $.now is rw; |
|||
has $.value is rw = 0; |
|||
has $.integrator is rw; |
|||
method init() { |
|||
self.value = &(self.f)(0); |
|||
self.integrator = Thread.new( |
|||
:code({ |
|||
loop { |
|||
my $t1 = now; |
|||
self.value += (&(self.f)(self.now) + &(self.f)($t1)) * ($t1 - self.now) / 2; |
|||
self.now = $t1; |
|||
sleep .001; |
|||
} |
|||
}), |
|||
:app_lifetime(True) |
|||
).run |
|||
} |
|||
method Input (&f-of-t) { |
|||
self.f = &f-of-t; |
|||
self.init; |
|||
self.now = now; |
|||
} |
|||
method Output { self.value } |
|||
} |
|||
my $a = Integrator.new; |
|||
$a.Input( sub ($t) { sin(2 * π * .5 * $t) } ); |
|||
say "Initial value: ", $a.Output; |
|||
sleep 2; |
|||
say "After 2 seconds: ", $a.Output; |
|||
$a.Input( sub ($t) { 0 } ); |
|||
sleep .5; |
|||
say "f(0): ", $a.Output;</lang> |
|||
{{out|Typical output}} |
|||
<pre>Initial value: 0 |
|||
After 2 seconds: -0.0005555887464620366 |
|||
f(0): 0</pre> |
|||
=={{header|OxygenBasic}}== |
=={{header|OxygenBasic}}== |
||
Line 1,860: | Line 1,849: | ||
' |
' |
||
sleep 5 'hand control to OS for a while |
sleep 5 'hand control to OS for a while |
||
end |
end doA072954 |
||
print str(integral,4) |
print str(integral,4) |
||
Rudolpho.clear</lang> |
Rudolpho.clear</lang> |
||
=={{header|Oz}}== |
|||
<lang oz>declare |
|||
fun {Const X} |
|||
fun {$ _} X end |
|||
end |
|||
fun {Now} |
|||
{Int.toFloat {Property.get 'time.total'}} / 1000.0 |
|||
end |
|||
class Integrator from Time.repeat |
|||
attr |
|||
k:{Const 0.0} |
|||
s:0.0 |
|||
t1 k_t1 |
|||
t2 k_t2 |
|||
meth init(SampleIntervalMS) |
|||
t1 := {Now} |
|||
k_t1 := {@k @t1} |
|||
{self setRepAll(action:Update |
|||
delay:SampleIntervalMS)} |
|||
thread |
|||
{self go} |
|||
end |
|||
end |
|||
meth input(K) |
|||
k := K |
|||
end |
|||
meth output($) |
|||
@s |
|||
end |
|||
meth Update |
|||
t2 := {Now} |
|||
k_t2 := {@k @t2} |
|||
s := @s + (@k_t1 + @k_t2) * (@t2 - @t1) / 2.0 |
|||
t1 := @t2 |
|||
k_t1 := @k_t2 |
|||
end |
|||
end |
|||
Pi = 3.14159265 |
|||
F = 0.5 |
|||
I = {New Integrator init(10)} |
|||
in |
|||
{I input(fun {$ T} |
|||
{Sin 2.0 * Pi * F * T} |
|||
end)} |
|||
{Delay 2000} %% ms |
|||
{I input({Const 0.0})} |
|||
{Delay 500} %% ms |
|||
{Show {I output($)}} |
|||
{I stop}</lang> |
|||
=={{header|Perl}}== |
|||
<lang perl>#!/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;</lang> |
|||
=={{header|Perl 6}}== |
|||
{{works with|Rakudo|2018.12}} |
|||
There is some jitter in the timer, but it is typically accurate to within a few thousandths of a second. |
|||
<lang perl6>class Integrator { |
|||
has $.f is rw = sub ($t) { 0 }; |
|||
has $.now is rw; |
|||
has $.value is rw = 0; |
|||
has $.integrator is rw; |
|||
method init() { |
|||
self.value = &(self.f)(0); |
|||
self.integrator = Thread.new( |
|||
:code({ |
|||
loop { |
|||
my $t1 = now; |
|||
self.value += (&(self.f)(self.now) + &(self.f)($t1)) * ($t1 - self.now) / 2; |
|||
self.now = $t1; |
|||
sleep .001; |
|||
} |
|||
}), |
|||
:app_lifetime(True) |
|||
).run |
|||
} |
|||
method Input (&f-of-t) { |
|||
self.f = &f-of-t; |
|||
self.init; |
|||
self.now = now; |
|||
} |
|||
method Output { self.value } |
|||
} |
|||
my $a = Integrator.new; |
|||
$a.Input( sub ($t) { sin(2 * π * .5 * $t) } ); |
|||
say "Initial value: ", $a.Output; |
|||
sleep 2; |
|||
say "After 2 seconds: ", $a.Output; |
|||
$a.Input( sub ($t) { 0 } ); |
|||
sleep .5; |
|||
say "f(0): ", $a.Output;</lang> |
|||
{{out|Typical output}} |
|||
<pre>Initial value: 0 |
|||
After 2 seconds: -0.0005555887464620366 |
|||
f(0): 0</pre> |
|||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |