Nasal Loops: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
m (Use Nasal highlighter)
Line 7: Line 7:




<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
for( preloop_initialization; # will be run prior to the first invocation of the loop, usually to initialize a loop counter
for( preloop_initialization; # will be run prior to the first invocation of the loop, usually to initialize a loop counter
     condition_during_loop;  # will be run prior to each iteration, usually to check the loop counter  and cancel the loop if false
     condition_during_loop;  # will be run prior to each iteration, usually to check the loop counter  and cancel the loop if false
Line 18: Line 18:




<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
for (var i=0; i < 3; i = i+1) {
for (var i=0; i < 3; i = i+1) {
     # loop body
     # loop body
Line 30: Line 30:
The differences are that there is no <tt>do{}while()</tt>; construct, and there is a foreach/forindex, which takes a local variable name as its first argument and a vector as its second:
The differences are that there is no <tt>do{}while()</tt>; construct, and there is a foreach/forindex, which takes a local variable name as its first argument and a vector as its second:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
foreach(elem; list1) { doSomething(elem); }  # NOTE: the delimiter is a SEMICOLON ;
foreach(elem; list1) { doSomething(elem); }  # NOTE: the delimiter is a SEMICOLON ;
</syntaxhighlight>
</syntaxhighlight>
Line 38: Line 38:
The hash/vector index expression (one that uses brackets) is an lvalue that can be assigned as well as inspected:
The hash/vector index expression (one that uses brackets) is an lvalue that can be assigned as well as inspected:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
foreach(light; lights) { lightNodes[light] = propertyPath; }
foreach(light; lights) { lightNodes[light] = propertyPath; }
</syntaxhighlight>
</syntaxhighlight>
Line 44: Line 44:
To walk through all elements of a hash, use a foreach loop on the keys of they hash. Then you call pull up the values of the hash using the key.  Example:
To walk through all elements of a hash, use a foreach loop on the keys of they hash. Then you call pull up the values of the hash using the key.  Example:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
myhash = {first: 1000, second: 250, third: 25.2 };
myhash = {first: 1000, second: 250, third: 25.2 };
foreach (var i; keys (myhash)) {
foreach (var i; keys (myhash)) {
Line 60: Line 60:
There is also a "forindex", which is like foreach except that it assigns the ''index'' of each element, instead of the value, to the loop variable.
There is also a "forindex", which is like foreach except that it assigns the ''index'' of each element, instead of the value, to the loop variable.


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
forindex(i; list1) { doSomething(list1[i]); }
forindex(i; list1) { doSomething(list1[i]); }
</syntaxhighlight>
</syntaxhighlight>
Line 66: Line 66:
Also, braceless blocks work for loops equally well:
Also, braceless blocks work for loops equally well:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
var c = 0;
var c = 0;
while( c < 5 )
while( c < 5 )
Line 78: Line 78:
For operations that should continue over a longer period, one needs a non-blocking solution. This is done by letting functions call themselves after a timed delay:  
For operations that should continue over a longer period, one needs a non-blocking solution. This is done by letting functions call themselves after a timed delay:  


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
var loop = func {
var loop = func {
     print("this line appears once every two seconds");
     print("this line appears once every two seconds");
Line 91: Line 91:
The fewer code FlightGear has to execute, the better, so it is desirable to run loops only when they are needed. But how does one stop a loop? A once triggered timer function can't be revoked. But one can let the loop function check an outside variable and refuse calling itself, which makes the loop chain die off:
The fewer code FlightGear has to execute, the better, so it is desirable to run loops only when they are needed. But how does one stop a loop? A once triggered timer function can't be revoked. But one can let the loop function check an outside variable and refuse calling itself, which makes the loop chain die off:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
var running = 1;
var running = 1;
var loop = func {
var loop = func {
Line 111: Line 111:
This can be solved by providing each loop chain with a ''loop identifier'' and letting the function end itself if the id doesn't match the global loop-id. Self-called loop functions need to inherit the chain id. So, every time the global loop id is increased, all loop chains die, and a new one can immediately be started.
This can be solved by providing each loop chain with a ''loop identifier'' and letting the function end itself if the id doesn't match the global loop-id. Self-called loop functions need to inherit the chain id. So, every time the global loop id is increased, all loop chains die, and a new one can immediately be started.


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
  var loopid = 0;
  var loopid = 0;
  var loop = func(id) {
  var loop = func(id) {

Revision as of 21:09, 10 November 2013


Nasal has several ways to implement an iteration.

for, while, foreach, and forindex loops

Nasal's looping constructs are mostly C-like:


for( preloop_initialization; # will be run prior to the first invocation of the loop, usually to initialize a loop counter
     condition_during_loop;  # will be run prior to each iteration, usually to check the loop counter  and cancel the loop if false
     post_iteration_expression # will be run after each iteration, usually to increment a loop counter
   ) 
{
    # loop body
}


for (var i=0; i < 3; i = i+1) {
    # loop body
}

while (condition) {
    # loop body
}

The differences are that there is no do{}while(); construct, and there is a foreach/forindex, which takes a local variable name as its first argument and a vector as its second:

foreach(elem; list1) { doSomething(elem); }  # NOTE: the delimiter is a SEMICOLON ;

In other words, even though foreach or forindex look like function calls, they work differently and they have braces after the parentheses.

The hash/vector index expression (one that uses brackets) is an lvalue that can be assigned as well as inspected:

foreach(light; lights) { lightNodes[light] = propertyPath; }

To walk through all elements of a hash, use a foreach loop on the keys of they hash. Then you call pull up the values of the hash using the key. Example:

myhash = {first: 1000, second: 250, third: 25.2 };
foreach (var i; keys (myhash)) {
  #multiply each value by 2:
  myhash[i] *= 2; 
  #print the key and new value:
  print (i, ": ", myhash[i]);
}
# this will print in some order:
#first: 2000
#second: 250
#thid: 25.2

There is also a "forindex", which is like foreach except that it assigns the index of each element, instead of the value, to the loop variable.

forindex(i; list1) { doSomething(list1[i]); }

Also, braceless blocks work for loops equally well:

var c = 0;
while( c < 5 )
  print( c += 1 );
print("end of loop\n");

settimer loops

Loops using while, for, foreach, and forindex block all of FlightGear's subsystems that run in the main thread for the duration of the loop body, and can, thus, only be used for instantaneous operations that don't take too long.

For operations that should continue over a longer period, one needs a non-blocking solution. This is done by letting functions call themselves after a timed delay:

var loop = func {
    print("this line appears once every two seconds");
    settimer(loop, 2);
}

loop();        # start loop

Note that the settimer function expects a function object (loop), not a function call (loop()) (though it is possible to make a function call return a function object--an advanced functional programming technique that you won't need to worry about if you're just getting started with Nasal).

The fewer code FlightGear has to execute, the better, so it is desirable to run loops only when they are needed. But how does one stop a loop? A once triggered timer function can't be revoked. But one can let the loop function check an outside variable and refuse calling itself, which makes the loop chain die off:

var running = 1;
var loop = func {
    if (running) {
        print("this line appears once every two seconds");
        settimer(loop, 2);
    }
}

loop();        # start loop ...
...
running = 0;   # ... and let it die

Loop Identifiers

Unfortunately, this method is rather unreliable. What if the loop is "stopped" and a new instance immediately started again? Then the running variable would be 1 again, and a pending old loop call, which should really finish this chain, would happily continue. And the new loop chain would start, too, so that we would end up with two loop chains.

This can be solved by providing each loop chain with a loop identifier and letting the function end itself if the id doesn't match the global loop-id. Self-called loop functions need to inherit the chain id. So, every time the global loop id is increased, all loop chains die, and a new one can immediately be started.

 var loopid = 0;
 var loop = func(id) {
     id == loopid or return;           # stop here if the id doesn't match the global loop-id
     ...
     settimer(func { loop(id) }, 2);   # call self with own loop id
 }
 
 loop(loopid);       # start loop
 ...
 loopid += 1;        # this kills off all pending loops, as none can have this new identifier yet
 ...
 loop(loopid);       # start new chain; this can also be abbreviated to:  loop(loopid += 1);

Beginning with FlightGear 2.11+ you should consider using the maketimer() API instead.

More information about the settimer function