For and While Loops
For i := a Do expr
The For loop successively assigns the next atom from array a to local index i
, and evaluates expression expr. expr might refer to i
, for xample to slice out a particular element of an array. a
might be a list of values defined by m..n
or Sequence(m, n, dx)
or it might be a multidimensional array. Normally, it evaluates the body expr
once for each atom in a
.
The result of the For loop is an array with all the indexes of a
containing the values of each evaluation of expr
. If any or all evaluations of expr
have any additional index(es), they are also indexes of the result.
Usually, the Intelligent Array features take care of iterating over indexes of arrays without the need for explicit looping.
<Tip Title="Tip> Analytica’s Intelligent Array features means that you rarely need explicit iteration using For loops to repeat operations over each dimensions of an array, often used in conventional computer language. If you find yourself using For loops a lot in Analytica, this might be a sign that you are not using Intelligent Arrays effectively. If so, please (re)read the sections on Intelligent Arrays.</Tip>
A For loop is sometimes useful in these specialized cases:
- To avoid selected evaluations of
expr
that might be invalid or out of range, and can be prevented by nesting anIf-Then-Else
inside a For. - To apply an Analytica function that requires an atom or one- or two-dimensional array input to a higher-dimensioned array.
- To reduce the memory needed f or calculations with very large arrays by reducing the memory requirement for intermediate results.
See below for an example of each of these three cases.
Library Special
Avoiding out-of-range errors
Consider the following expression:
If x<0 Then 0 Else Sqrt(x)
The If-Then-Else
is included in this expression to avoid the warning “Square root of a negative number.” However, if x
is an array of values, this expression cannot avoid the warning since Sqrt(x) is evaluated before If-Then-Else
selects which elements of Sqrt(x) to include. To avoid the warning (assuming x
is indexed by i
), the expression can be rewritten as:
For j:=I do
If x[i=j]<0 then 0 else Sqrt(x[i=j])
Or as (see next section):
Using y:=x in i do
If y<0 Then 0 else Sqrt(y)
Situations like this can often occur during slicing operations. For example, to shift x one position to the right along i, the following expression would encounter an error:
if i<2 then x[i=1] else x[i=i-1]
The error occurs when x[i=i-1] is evaluated since the value corresponding to i-1=0 is out of range. The avoid the error, the expression can be rewritten as:
For j:=i do
If j<2 then x[i=1] else x[i=j-1]
Out-of-range errors can also be avoided without using For by placing the conditional inside an argument. For example, the two examples above can be written without For as follows:
Sqrt(if x<0 then 0 else x)
x[i=(if i<2 then 1 else i-1)]
Dimensionality reduction
For can be used to apply a function that requires an atom, one- or two- dimensional input to a multi-dimensional result. This usage is rare in Analytica since array abstraction normally does this automatically; however, the need occasionally arises in some circumstances.
Suppose you have an array A
indexed by I
, and you wish to apply a function f(x)
to each element of A
along I
. In a conventional programming language, this would require a loop over the elements of A
; however, in almost all cases, Analytica’s array abstraction does this automatically — the expression is simply f(A)
, and the result remains indexed by i
. However, there are a few cases where Analytica does not automatically array abstract, or it is possible to write a user-defined function that does not automatically array abstract (e.g., by declaring a parameter to be of type Atom
, as detailed in Parameter Qualifiers). For example, Analytica does not array abstract over functions such as Sequence, Split, Subset, or Unique, since these return un-indexed lists of varying lengths that are unknown until the function evaluates. Suppose we have the following variables defined (note that A
is an array of text values):
- A: Index_1 ▼
1 1 A, B, C 2 D, E, F 3 G, H, I
- Index_1 ▼
1 2 3
We wish to split the text values in A and obtain a two dimensional array of letters indexed by Index_1 and Index_2. Since Split does not array abstract, we must do each row separately and re-index by Index_2 before the result rows are recombined into a single array. This is accomplished by the following loop:
FOR Row := Index_1 DO Array(Index_2, SplitText(A[Index_1=Row], ','))
This results in:
- Index_1 ▼, Index_2 ▶
1 2 3 1 A B C 2 D E F 3 G H I
Reducing memory requirements
In some cases, it is possible to reduce the amount of memory required for intermediate results during the evaluation of expressions involving large arrays. For example, consider the following expression:
MatrixA:
A two dimensional array indexed by M and N.
MatrixB:
A two dimensional array indexed by N and P.
Average(MatrixA * MatrixB, N)
During the calculation, Analytica needs memory to compute MatrixA * MatrixB
, an array indexed by M
, N
, and P
. If these indexes have sizes 100, 200, and 300 respectively, then MatrixA * MatrixB
contains 6,000,000 numbers, requiring over 60 megabytes of memory at 10
bytes per number.
To reduce the memory required, use the following expression instead:
For L := M Do Average(MatrixA[M=L]*MatrixB, N)
Each element MatrixA[M=L]*MatrixB
has dimensions N
and P
, needing only 200x300x10= 600 kilobytes of memory at a time.
Sum(a*b, i)
, it performs a similar transformation internally.While(Test) Do Body
While evaluates Body repeatedly as long as Test <> 0. For While ... to terminate, Body must produce a side-effect on a local variable that is used by Test, causing Test eventually to equal 0. If Test never becomes False, While continues to loop indefinitely. If you suspect that might be happening, type Control+. (Control+period) to interrupt execution.
Test must evaluate to an atomic (non-array) value; therefore, it is a good idea to force any local variable used in Test to be atomic valued. While is one of the few constructs in Analytica that does not generalize completely to handle arrays. But, there are ways to ensure that variables and functions using While support Intelligent Arrays and probabilistic evaluation. See “While and array abstraction” on page 386 for details.
While returns the final value found in the last iteration of Body or Null if no iterations occur. For example: (Var x := 1; While x < 10 Do x := x+1) ¨ 10 (Var x := 1; While x > 10 Do x := x+1) ¨ Null
Using While often follows the following pattern:
Var x[]:= ...; While (FunctionOf(x)) Do ( ... x := expr; ... ); returnValue
Enable comment auto-refresher