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 }
where 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 }
while 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
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
SET x=(x > 0) ? SQRT(x) : 0where 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 }
or
FOREACH variable { list } { command_list }
where 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 }
will print out:
one 2 threeForeach loops may be nested, but again the variables must be distinct. You can delimit the list with
{} 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 } ELSE { list2 }
where 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 aa}
which 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 }
set i=0 repeat aa while i < 10
it 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 }
$!!1
_$!!1}
_$1
macro _$1 delete
and is one of SM's default macros (type "help repeat" if you don't
believe me).