Difference between revisions of "For..Do"
(final refinements on examples) |
(Added various hyperlinks) |
||
Line 5: | Line 5: | ||
For each successive value of ''I'', assigns that value to the local variable ''Temp'', and evaluates expression ''Expr''. ''Expr'' may refer to ''I'' and ''Temp''. ''Temp'' becomes a local variable that can be referred to only within the expression ''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 ''Temp''. ''Temp'' becomes a local 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. | + | 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 Arrays|self-indexed variable]], Temp is set successively to each of ''I'''s [[IndexValue|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. | + | (new to 4.0) Prior to Analytica 4.0, when I was a 1-D [[Self-Indexed Arrays|self-indexed variable]], the [[Evaluation Modes|context value]] of I, rather than the [[IndexValue|index value]] of I, was used. |
= For Temp := loopExpr Do bodyExpr = | = 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 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 [[Evaluation Modes|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[I,J] := loopExpr Do bodyExpr = | ||
Line 35: | Line 35: | ||
= Detailed Notes = | = Detailed Notes = | ||
− | When looping over a self-indexed variable, there is a subtlety as to whether the index value or main value of the variable is used. Consider the following two variations: | + | When looping over a [[Self-Indexed Arrays|self-indexed variable]], there is a subtlety as to whether the index value or main value of the variable is used. Consider the following two variations: |
For Temp := X Do expr | For Temp := X Do expr | ||
For Temp[] := X Do expr | For Temp[] := X Do expr | ||
− | When X is a self-indexed variable, there is a subtle difference between these two variations. The first case loops over the index values of X, while the second case loops over the main value of X. Consider the following self-indexed variable, X, indexed by Self and In1: | + | When X is a [[Self-Indexed Arrays|self-indexed variable]], there is a subtle difference between these two variations. The first case loops over the index values of X, while the second case loops over the main value of X. Consider the following self-indexed variable, X, indexed by Self and In1: |
{| border="1" | {| border="1" | ||
Line 57: | Line 57: | ||
The For Temp:=X variation loops three times, setting Temp to 1, 2, and 3. The For Temp[]:=X variation loops 12 times, settings X to 11, 12, .., 33, 34. | The For Temp:=X variation loops three times, setting Temp to 1, 2, and 3. The For Temp[]:=X variation loops 12 times, settings X to 11, 12, .., 33, 34. | ||
− | Other than this subtlety, for expressions where X is not a self-indexed variable, the two syntaxes produce the same result. | + | Other than this subtlety, for expressions where X is not a [[Self-Indexed Arrays|self-indexed variable]], the two syntaxes produce the same result. |
− | (new to 4.0) Prior to 4.0, For ''Temp:=X'' required X to be an index or sequence, but used X's value rather than index value. Analytica 4.0 loops all elements of X for any dimensionality. | + | (new to 4.0) Prior to 4.0, For ''Temp:=X'' required X to be an index or sequence, but used X's value rather than [[IndexValue|index value]]. Analytica 4.0 loops all elements of X for any dimensionality. |
Line 94: | Line 94: | ||
''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.'' | ''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 | + | 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|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. For example, Analytica does not [[Array Abstraction|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): |
{| border="1" | {| border="1" | ||
Line 105: | Line 105: | ||
|} | |} | ||
− | 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. | + | 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 | for Row:=Index_1 do | ||
Line 129: | Line 129: | ||
Consider the following expression: | Consider the following expression: | ||
− | If IsNumber(X) then Mod(X,2) else 0 | + | If [[IsNumber]](X) then [[Mod]](X,2) else 0 |
− | The If-Then-Else is included here to avoid the error "the first parameter to Mod must be numeric". However, when X is an array of values, this expression may not avoid the error because Mod(X,2) will if any element of X is numeric, and will encounter the error if any element of X is non-numeric. To avoid the error, the expression can be re-written as | + | The [[If-Then-Else]] is included here to avoid the error "the first parameter to Mod must be numeric". However, when X is an array of values, this expression may not avoid the error because [[Mod]](X,2) will if any element of X is numeric, and will encounter the error if any element of X is non-numeric. To avoid the error, the expression can be re-written as |
For j:=X do | For j:=X do | ||
− | If IsNumber(j) then Mod(j,2) else 0 | + | If [[IsNumber]](j) then [[Mod]](j,2) else 0 |
− | In most cases of this form where a warning results, Analytica can figure out whether the warning impacts the final result without an explicit For loop. For example, even though Sqrt(X) or X[I=I-1] may cause a warning for some elements of X when X is an array, the expressions | + | In most cases of this form where a warning results, Analytica can figure out whether the warning impacts the final result without an explicit For loop. For example, even though [[Sqrt]](X) or [[Slice/Subscript Operator|X[I=I-1] ]] may cause a warning for some elements of X when X is an array, the expressions |
− | If X<0 Then 0 Else Sqrt(X) | + | If X<0 Then 0 Else [[Sqrt]](X) |
− | if I<2 then X[I=1] else X[I=I-1] | + | if I<2 then [[Slice/Subscript Operator|X[I=1] ]] else [[Slice/Subscript Operator|X[I=I-1] ]] |
do not need an explicit For loops to avoid the warnings. In releases of Analytica prior to Analytica 3.0, For loops were necessary to avoid the warnings. | do not need an explicit For loops to avoid the warnings. In releases of Analytica prior to Analytica 3.0, For loops were necessary to avoid the warnings. | ||
Line 160: | Line 160: | ||
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: | 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. | + | :''MatrixA'': A two dimensional array indexed by M and N. |
− | :MatrixB: A two dimensional array indexed by N and P. | + | :''MatrixB'': A two dimensional array indexed by N and P. |
Average(MatrixA * MatrixB, N) | 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. | + | 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 | To reduce the memory required, use the following expression instead | ||
− | For L:=M Do Average(MatrixA[M=L]*MatrixB,N) | + | 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. | + | 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, where an expression has the form Sum(A*B,I), Analytica performs a similar transformation internally.'' | + | '''''Analytica Note''''': ''For the special case of a dot product, where an expression has the form [[Sum]](A*B,I), Analytica performs a similar transformation internally.'' |
Revision as of 21:53, 22 February 2007
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 Temp. Temp becomes a local 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
In this usage of For, with indexes of Temp explicitly specified, loopExpr is evaluated, and then For loops over all indexes of this result that are not listed, leaving Temp indexed by those that are 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. With an empty list of indexes, For Temp[]:=loopExpr, For loops over all dimensions of loopExpr, so that Temp is atomic in each iteration.
When to use a For loop
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 update a local variable as a side-effect of each iteration.
- To apply a non-abstractable Analytica function that requires a scalar or one or two-dimensional array input to a higher-dimensioned array.
- To avoid the attempted evaluation of out-of-range values by nesting an If-Then-Else inside a For.
- To avoid expensive conditional computations.
- To reduce the memory needed for calculations with very large arrays by reducing the memory requirement for intermediate results.
Your Analytica expressions will be easily to read and understand, and evaluate much more rapidly, if you avoid Vacuous For Loops. A vacuous loop is an expression in which Analytica would automatically array abstract on its own without the For loop.
Library
Special
Detailed Notes
When looping over a self-indexed variable, there is a subtlety as to whether the index value or main value of the variable is used. Consider the following two variations:
For Temp := X Do expr For Temp[] := X Do expr
When X is a self-indexed variable, there is a subtle difference between these two variations. The first case loops over the index values of X, while the second case loops over the main value of X. Consider the following self-indexed variable, X, indexed by Self and In1:
X: | In1 | ||||
---|---|---|---|---|---|
'a' | 'b' | 'c' | 'd' | ||
X | 1 | 11 | 12 | 13 | 14 |
2 | 21 | 22 | 23 | 24 | |
3 | 31 | 32 | 33 | 34 |
The For Temp:=X variation loops three times, setting Temp to 1, 2, and 3. The For Temp[]:=X variation loops 12 times, settings X to 11, 12, .., 33, 34.
Other than this subtlety, for expressions where X is not a self-indexed variable, the two syntaxes produce the same result.
(new to 4.0) Prior to 4.0, For Temp:=X required X to be an index or sequence, but used X's value rather than index value. Analytica 4.0 loops all elements of X for any dimensionality.
There is another equivalence in Analytica. Consider the following two expressions:
For Temp[I,J] := X Do expr Var Temp[I,J] := X Do expr
Although these may be conceptualized differently, the first as a procedural expression, the second as a declarative expression, they are functionally identical. The first says to loop over all dimensions of X except I and J. The second declares Temp to be a local variable indexed only by I and J while expr is evaluated. In order to guarantee that Temp is indexed only by I and J, Analytica will iterate over all dimensions in X other than I and J. Hence, these two are functionally identical. In general, declarative expressions tend to be clearer and conceptually simpler, allowing the modeller or reader to express their logic and letting the software worry about the evaluation details; therefore, in most cases the Var..Do is the preferred form.
There is an alternative syntax that may be seen in older models and is still supported. The following two syntax forms are treated identically:
For Temp[I,J] := X Do expr For Temp := X in each I,J Do expr
Examples
Using For loops for their size-effects
The following loop computes ratios of successive Fibonacci numbers (which converges to the golden ratio):
var a:=0; var b:=1; for i:=1..100 do ( var c := a+b; a := b; b := c; a/b )
The result of each iteration through the loop depends on side-effects in the local variables a and b that were updated in the previous iteration.
Abstracting over Non-Abstractable Functions
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. 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):
Index_1 | 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
Index_2 → | ||||
---|---|---|---|---|
1 | 2 | 3 | ||
Index_1 | 1 | A | B | C |
2 | D | E | F | |
3 | G | H | I |
Avoiding Out-Of-Range or Type Mismatch Errors
Consider the following expression:
If IsNumber(X) then Mod(X,2) else 0
The If-Then-Else is included here to avoid the error "the first parameter to Mod must be numeric". However, when X is an array of values, this expression may not avoid the error because Mod(X,2) will if any element of X is numeric, and will encounter the error if any element of X is non-numeric. To avoid the error, the expression can be re-written as
For j:=X do If IsNumber(j) then Mod(j,2) else 0
In most cases of this form where a warning results, Analytica can figure out whether the warning impacts the final result without an explicit For loop. For example, even though Sqrt(X) or X[I=I-1] may cause a warning for some elements of X when X is an array, the expressions
If X<0 Then 0 Else Sqrt(X) if I<2 then X[I=1] else X[I=I-1]
do not need an explicit For loops to avoid the warnings. In releases of Analytica prior to Analytica 3.0, For loops were necessary to avoid the warnings.
Expensive Conditional Computation
Suppose you have user-defined functions, f(X,I) and g(X,I), that are very computationally expensive, and a criteria, SomeCondition(A,I) to determine which is the most appropriate to use. If you evaluate:
If SomeCondition(A,I) Then f(A,I) Else g(A,I)
when A is indexed by I and J, then f(A,I) and g(A,I) are both evaluated for every slice of J, even though only one or the other is used at every slice. A For loop can avoid unnecessary evaluations of f and g:
For X[I] := A Do If SomeCondition(X,I) Then f(X,I) Else g(X,I)
For small to medium computations, the computational overhead of the For loop will far outweight the saving in avoiding function evaluations. In the absence of a For loop, Analytica's array abstraction is able to carry out operations over entire arrays in native code, rather than having to interpret a For loop, and is thus much faster. So, this improvement should only be used when the computational cost of f(X,I) or g(X,I) is substantial. See Vacuous For Loops.
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, where an expression has the form Sum(A*B,I), Analytica performs a similar transformation internally.
Enable comment auto-refresher