Go to the previous, next section.

# DO and FOREACH loops, and IF statements

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 2
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 2
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@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 2
or
FOREACH variable { list 2 { command_list 2
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 2
will print out:
one
2
three
Foreach 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 2
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2
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 2
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 2
\$!!1
_\$!!1}
_\$1
macro _\$1 delete
and is one of SM's default macros (type "help repeat" if you don't believe me).

Go to the previous, next section.