Go to the previous, next section.
Related to the macro facility are the DO
and FOREACH
commands.
IF
is included here as a flow-of-control keyword. There are no
while or until loops in SM (and no goto's) but it is easy enough to write
them as macros, as we shall see.
The syntax for a DO
loop is
DO variable = expr1 , expr2 [ , expr3 ] { command_list 2where the third expression is optional, defaulting to 1. The value of variable (
$variable
) is in turn set to expr1
, expr1+exp3
, ...,
expr2
, and the
commands in command_list
executed. Changing the value of $variable
within the command list
has no effect upon the loop. Do loops may be nested, but the name of
the variable in each such loop must be distinct. A trivial example
would be
DO val = 123, 123+10, 2 { WRITE STANDARD $val 2while a more interesting example would be the macro
square
discussed in the section on examples.
Because the body of the loop must be scanned (and parsed) repeatedly, loops
with many circuits are rather slow. If at all possible you should try
to use vector operations rather than DO
loops.
For example the loop@example
DO i=0,DIMEN(x)-1 {
SET x[$i]=SQRT(x[$i]) IF(x[$i] > 0)
SET x[$i]=0 IF(x[$i] <= 0)
}
is better written as @example
SET x=(x > 0) ? SQRT(x) : 0
where the ternary operator ?:
is discussed in the section on vectors
(see section Vectors and Arithmetic).
Foreach loops are similar, with syntax
FOREACH variable ( list ) { command_list 2or
FOREACH variable { list 2 { command_list 2where the list may consist of a number of words or numbers. Each element in the list is in turn defined to be the value of
$variable
, and then the commands in command_list
are executed,
so that for example the commands:
FOREACH i ( one 2 three ) { WRITE STANDARD $i 2will print out:
one 2 threeForeach loops may be nested, but again the variables must be distinct. You can delimit the list with
{2
so that it can include
keywords (and other things that you want treated as strings such as 0.1
or $date
), but even then you can't have the word delete
in the
list of a foreach. Sorry.
If statements look similar, with syntax
IF ( expr ) { list 2 ELSE { list2 2where the
ELSE
clause is optional, but if it is omitted the closing
} must be followed by a newline (or explicit \n)
(see section The Command Interpreter).
If the (scalar) expression is true (i.e. non-zero), then the commands
list
are executed, otherwise list2
is, if present. It is also
possible to use IF
statements directly in plotting commands, for
example POINTS x y IF(z > 1/x)
.
The way to write general loops in SM is to make use of tail-recursive macros. The simplest example would be
macro aa {echo hello, world\n aa2which prints
hello, world
and then calls itself, so it prints
hello, world
and then calls itself, and so on until you hit ^C.
The absence of
a space before the closing brace is very important, as it allows SM to
discard the macro before calling it again, which means that it won't fill
up its call stack and start complaining. A more interesting example is
the macro repeat
which repeats a given macro while the given
condition is true. For example, if you say
macro aa { set i=i+1 calc i 2 set i=0 repeat aa while i < 10it will print the integers from 0 to 9. With a few checks, bells, and whistles the macro looks like:
repeat 103 # Repeat a macro `name' while the condition is true # syntax: repeat name while condition # Example: set i=0 repeat body while i < 10 if('$2' != 'while') { echo Syntax: $0 macro while condition return } if(int((whatis($1)-4*int(whatis($1)/4))/2) == 0) { echo $1 is not a macro return } macro _$1 { if(($!!3) == 0) { return 2 $!!1 _$!!1} _$1 macro _$1 deleteand is one of SM's default macros (type "help repeat" if you don't believe me).
Go to the previous, next section.