For..Do
For Temp:= I Do Expr
For each successive value of I, assigns that value to the local variable Temp, and evaluates expression Expr. Expr may refer to I and/or Temp. Temp is a local or temporary variable that can be referred to only within the expression Expr.
In the most common usage, I is an index, and the result of evaluating the For loop is an array indexed by I, with each slice along I containing the result of evaluating Expr at that value. When I is a self-indexed variable, Temp is set successively to each of I's index values.
(new to 4.0) Prior to Analytica 4.0, when I was a 1-D self-indexed variable, the context value of I, rather than the index value of I, was used.
For Temp := loopExpr Do bodyExpr
For can also be used to loop over every element of an array result, with loopExpr being an arbitrary expression. If loopExpr is an identifer, and that identifier is a valid index, then its index value is used (as described in the previous section), otherwise loopExpr is evaluated in context, and Temp loops over all elements of the array result. The result of evaluating For in this case is an array containing all of the dimensions of loopExpr, with each slice among those dimensions being the result of evaluating bodyExpr.
For Temp[I,J,..] := loopExpr Do bodyExpr
For Temp := loopExpr In Each I,J,K Do bodyExpr
These two variations of For, where indexes of Temp are explicitly specified, evaluates loopExpr, and then loops over all indexes of this result that are not listed. For example, if the result of loopExpr is indexed by I,J,K and L, then For Temp[I,J] loops over all combinations of K,L. At each iteration, a slice along I and J is assigned to Temp, which in this case would itself be an array indexed by K and L. Two alternative syntactic forms may be written, both treated equivalently.
If you make appropriate use of the intelligent array features described earlier in this and preceding chapters, you will rarely need to use For structure (unlike in conventional computer languages, which require extensive use of For loops and related control structures for handling arrays). For is sometimes useful in these specialized cases:
• To avoid the attempted evaluation of out-of-range values by nesting an If-Then-Else inside a For. • To apply an Analytica function that requires a scalar or oneor two-dimensional array input to a higher-dimensioned array. • To reduce the memory needed for calculations with very large arrays by reducing the memory requirement for intermediate results.
Library
Special
Examples
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 may not 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):
Var 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 a scalar, one- or two- dimensional input to a multidimensional 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), 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 Scalar, see page 422). For example, Analytica does not array abstract over functions such as Sequence, Split, Subset, or Unique, since these return unindexed lists of varying lengths that are unknown until the function evaluates. Suppose we have the following Variables defined (note: A is an array of text values):
1 | A,B,C |
---|---|
2 | D,E,F |
3 | G,H,I |
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 reindex 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,Split(A[Index_1=Row],’,’))
resulting in
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.
Analytica Note: For the special case of a dot product (see “Dot product of two matrices” on page 272), where an expression has the form Sum(A*B,I), Analytica performs a similar transformation internally.
Enable comment auto-refresher