### Loops

SPL supports C/C++ like while and for loops. The while statement takes on the following form:

while (expression)

statement;

The expression is evaluated and if it is non-zero, statement is executed and expression is re-evaluated. This cycle continues until expression becomes zero, at which point execution resumes after statement.

Compound statements are grouped using { }.

while (expression)

{

statement1;

statement2;

}

The srt function uses a while loop to calculate the square root of a real number using Newton’s approximation:

/*

*  square root of a real number by Newton's

*  approximation, accurate to about 5 decimal places

*/

srt(num)

{

local a1, b1;

/* check domain */

if (num <= 0)

{

return(0);

}

if (imag(num) != 0.0)

{

return(0);

}

b1 = num / 2;

a1 = num;

while (a1 > 0.00001 * b1)

{

a1 = abs((num / b1) - b1);

b1 = ((num / b1) + b1) / 2;

}

return(b1);

}

The for statement:

for (expr1; expr2; expr3)

statement;

is equivalent to:

expr1;

while (expr2)

{

statement;

expr3;

}

Like C/C++, SPL uses semicolons to separate expressions in a for loop. Expressions in for loops can be quite general, but the following form is generally used:

for (initialization; test criteria; loop increment)

{

statements controlled by loop;

}

The first expression uses an assignment operator on one or more variables. If more than one variable is initialized, each must be separated by commas. The second expression uses some form of relational operator to test when the loop should terminate. The third expression often consists of incrementing or decrementing one or more loop variables. For example:

/* raise x to the nth power for n > 0 */

power(x, n)

{

local p;

for (p = 1; n > 0; --n)

{

p *= x;

}

return(p);

}

The do-while loop tests the loop condition at the bottom after each pass through the loop body. The loop body is always executed at least once.

do

{

statements;

}

while (expression);

The statements are executed then expression is evaluated. If expression is non-zero, statements are executed again and again until expression evaluates to zero.

The simple loop iterator assigns a loop or iteration variable to the value of each row element of a series and executes the loop body after each iteration.

loop (var = array)

{

statements;

For example:

/* use loop to create Fibonocci numbers > 2 */

fibl(n)

{

local fib;

fib = {1, 1};

loop (j = 3..n)

{

fib[j] = fib[j-1] + fib[j-2];

}

return(fib);

}

The array can be any valid array. If the array has only one column, the iteration variable is assigned the next row element of the array after each iteration. If the array is multi-column, the variable is assigned the next column of the array after each iteration.

Unlike for or while, the loop variable cannot be modified during the iterations.

Because loop is a simpler and less flexible iteration construct, loop generally executes faster than for or while and is preferred for simple iterations where the loop variable is not modified.

A break statement causes the innermost enclosing loop to be exited immediately. For example, the following function implements a Shell sort:

/* sort series s into ascending order */

shellsort(s)

{

local v, n, gap, k, j, temp;

v = s;

n = length(v);

for (gap = int(n/2); gap > 0; gap = int(gap/2))

{

for (k = gap; k < n; k++)

{

for (j = k gap; j >= 0; j -= gap)

{

if (v[j+1] <= v[j+gap+1])

break;

temp       = v[j+1];

v[j+1]     = v[j+gap+1];

v[j+gap+1] = temp;

}

}

}

return(v)

}

There are three nested loops. The outermost loop controls the gap between compared elements, shrinking it from n/2 by a factor of two each pass until it becomes zero, The middle loop compares each pair of elements that is separated by gap; the innermost loop reverses any that are out of order. If the elements are in order, the inner loop terminates and control is resumed by the middle loop. Since gap is eventually reduced to one, all elements are eventually ordered correctly. The built-in length function returns the number of elements in a series.

The continue statement is related to break, but less often used; it causes the next iteration of the enclosing loop to begin. As an example, this function doubles only positive elements of a series, skipping negative values.

/* double positive values only */

pos2(a)

{

local n, j;

n = length(a);

for (j = 1; j <= n; j++)

{

if (a[j] < 0) continue; /* skip negative values */

/* do positive processing */

a[j] *= 2;

}

return(a);

}

Again, this function can be written more compactly as:

pos2(a)

{

local cond;

cond = a > 0;

return(2*a*cond + a*not(cond));

}

The goto statement transfers control directly to a labeled statement. The goto statement is useful for breaking out of deeply nested for or while loops:

a = 0;

for (j = 1; j < n; j++)

{

for (k = 1; k < j; k++)

{

for (l = 1; l < k; l++)

{

a += j * k * l;

if (a > n) goto exitpt;

}

}

}

exitpt:

return(a);

The break statement cannot be used here, since it breaks out of the innermost loop only. A label has the same form as a variable name and is followed by a colon. It can be attached to any statement in the same function as the goto.

The error function displays an optional message and aborts further SPL processing. For example:

/* invert a number */

invnum(x)

{

if (x == 0)

{

error("invnum - input must be non-zero");

}

else

{

return(1/x);

}

}

invnum(0) aborts and displays:

invnum - input must be non-zero

sqrt(invnum(0)) aborts and displays:

invnum - input must be non-zero

As shown in the second example, nested functions or SPL routines that call error, terminate execution.