Difference between revisions of "For and While Loops"

(Local Variables --> Local Values (terminology change))
 
(14 intermediate revisions by one other user not shown)
Line 4: Line 4:
 
== For i := a Do expr ==
 
== 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 example to [[slice]] out a particular element of an array. «a» might be a list of values defined by <code>m..n</code> 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 [[For]] loop successively assigns the next atom from array «a» to local index «i», and evaluates expression «expr». «expr» might refer to «i», for example to [[slice]] out a particular element of an array. «a» might be a list of values defined by [[Sequence_Operator|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.
 
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.
Line 10: Line 10:
 
Usually, the Intelligent Array features take care of iterating over indexes of arrays without the need for explicit looping.  
 
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|Intelligent Arrays]].</Tip>
+
<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:
 
A [[For]] loop is sometimes useful in these specialized cases:
  
* To avoid selected evaluations of <code>expr</code> that might be invalid or out of range, and can be prevented by nesting an <code>If-Then-Else</code> inside a [[For]].
+
* To avoid selected evaluations of «expr» that might be invalid or out of range, and can be prevented by nesting an [[If-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 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 for calculations with very large arrays by reducing the memory requirement for intermediate results.
 
* To reduce the memory needed for calculations with very large arrays by reducing the memory requirement for intermediate results.
Line 28: Line 28:
 
:<code>If x < 0 Then 0 Else Sqrt(x)</code>
 
:<code>If x < 0 Then 0 Else Sqrt(x)</code>
  
The <code>If-Then-Else</code> is included in this expression to avoid the warning “Square root of a negative number.” However, if <code>x</code> is an array of values, this expression cannot avoid the warning since [[Sqrt]](x) is evaluated before <code>If-Then-Else</code> selects which elements of [[Sqrt]]](x) to include. To avoid the warning (assuming <code>x</code> is indexed by <code>i</code>), the expression can be rewritten as:
+
The [[If-Then-Else]] is included in this expression to avoid the warning “Square root of a negative number.” However, if <code>x</code> 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 <code>x</code> is indexed by <code>i</code>), the expression can be rewritten as:
  
 
:<code>For j := I do</code>
 
:<code>For j := I do</code>
Line 42: Line 42:
 
:<code>if i < 2 then x[i = 1] else x[i = i - 1]</code>
 
:<code>if i < 2 then x[i = 1] else x[i = i - 1]</code>
  
The error occurs when <code>x[i = i-1]</code> is evaluated since the value corresponding to <code>i - 1 = 0</code> is out of range. The avoid the error, the expression can be rewritten as:
+
The error occurs when <code>x[i = i - 1]</code> is evaluated since the value corresponding to <code>i - 1 = 0</code> is out of range. The avoid the error, the expression can be rewritten as:
  
 
:<code>For j := i do
 
:<code>For j := i do
::If j < 2 then x[i = 1] else x[i = j-1]</code>
+
::If j < 2 then x[i = 1] else x[i = j - 1]</code>
  
 
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:
 
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:
Line 85: Line 85:
 
This results in:
 
This results in:
 
:{| class="wikitable"
 
:{| class="wikitable"
! !! colspan="3" style="text-align: left;" | Index_2 &#9654;
+
! !! colspan="3" | Index_2 &#9654;
 
|-
 
|-
! style="width:100px;" |'''Index_1 &#9660; '''
+
! '''Index_1 &#9660; '''
 
! 1
 
! 1
 
! 2
 
! 2
Line 97: Line 97:
 
|C
 
|C
 
|-
 
|-
! style="width:100px;" |2
+
! 2
|style="width:100px;" |D
+
|D
|style="width:100px;" |E
+
|E
|style="width:100px;" |F
+
|F
 
|-
 
|-
! style="width:100px;" |3
+
! 3
|style="width:100px;" |G
+
|G
|style="width:100px;" |H
+
|H
|style="width:100px;" |I
+
|I
 
|}
 
|}
 +
  
 
===Reducing memory requirements===
 
===Reducing memory requirements===
Line 114: Line 115:
 
:<code>MatrixA:</code> A two dimensional array indexed by M and N.
 
:<code>MatrixA:</code> A two dimensional array indexed by M and N.
 
:<code>MatrixB:</code> A two dimensional array indexed by N and P.
 
:<code>MatrixB:</code> A two dimensional array indexed by N and P.
:<code>Average(MatrixA * MatrixB, N)</code>
+
:<code>Average(MatrixA*MatrixB, N)</code>
  
During the calculation, Analytica needs memory to compute <code>MatrixA * MatrixB</code>, an array indexed by <code>M</code>, <code>N</code>, and <code>P</code>. If these indexes have sizes 100, 200, and 300 respectively, then <code>MatrixA * MatrixB</code> contains 6,000,000 numbers, requiring over 60 megabytes of memory at 10 bytes per number.
+
During the calculation, Analytica needs memory to compute <code>MatrixA*MatrixB</code>, an array indexed by <code>M</code>, <code>N</code>, and <code>P</code>. If these indexes have sizes 100, 200, and 300 respectively, then <code>MatrixA*MatrixB</code> 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:
Line 122: Line 123:
 
:<code>For L := M Do Average(MatrixA[M = L]*MatrixB, N)</code>
 
:<code>For L := M Do Average(MatrixA[M = L]*MatrixB, N)</code>
  
Each element <code>MatrixA[M = L]*MatrixB</code> has dimensions <code>N</code> and <code>P</code>, needing only 200x300x10= 600 kilobytes of memory at a time.
+
Each element <code>MatrixA[M = L]*MatrixB</code> has dimensions <code>N</code> and <code>P</code>, needing only 200x300x10 = 600 kilobytes of memory at a time.
  
 
<tip title="Tip">For the special case of a [[Matrix_functions#Dot_product_of_two_matrices|dot product]], for an expression of the form <code>Sum(a*b, i)</code>, it performs a similar transformation internally.</tip>
 
<tip title="Tip">For the special case of a [[Matrix_functions#Dot_product_of_two_matrices|dot product]], for an expression of the form <code>Sum(a*b, i)</code>, it performs a similar transformation internally.</tip>
Line 128: Line 129:
 
===While(Test) Do Body===
 
===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 <code>0</code>. If «Test» never becomes False, [[While]] continues to loop indefinitely. If you suspect that might be happening, type ''Control+. (Control+period)'' to interrupt execution.
+
[[While]] evaluates «Body» repeatedly as long as ''Test <> 0''. For [[While]] ... to terminate, «Body» must produce a side-effect on a local value that is used by «Test», causing «Test» eventually to equal <code>0</code>. If «Test» never becomes <code>False</code>, [[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  [[Ensuring Array Abstraction|While and array abstraction]].
+
«Test» must evaluate to an atomic (non-array) value; therefore, it is a good idea to force any local value 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  [[Ensuring Array Abstraction|While and array abstraction]].
  
[[While]]] returns the final value found in the last iteration of <code>Body</code> or <code>Null</code> if no iterations occur. For example:
+
[[While]] returns the final value found in the last iteration of <code>Body</code> or [[Null]] if no iterations occur. For example:
  
:<code>(Var x := 1; While x < 10 Do x := x+1) ¨ 10</code>
+
:<code>(Var x := 1; While x < 10 Do x := x + 1) ¨ 10</code>
:<code>(Var x := 1; While x > 10 Do x := x+1) ¨ Null</code>
+
:<code>(Var x := 1; While x > 10 Do x := x + 1) ¨ Null</code>
  
 
Using [[While]] often follows the following pattern:
 
Using [[While]] often follows the following pattern:
Line 157: Line 158:
  
 
== See Also ==
 
== See Also ==
* [http://wiki.analytica.com/index.php?title=Intelligent_Arrays Intelligent Arrays]
+
* [[Intelligent Arrays]]
 +
* [[For versus Var]]
 +
* [[Var..Do]]
 +
* [[For..Do]]
 +
* [[While..Do]]
 +
* [[Iterate]]
 +
* [[If-Then-Else]]
 +
* [[Looping over a model]]
 +
* [[Dynamic loop]]
  
  
<footer>Local Variables / {{PAGENAME}} / Recursion</footer>
+
<footer>Local Values / {{PAGENAME}} / Recursion</footer>

Latest revision as of 00:18, 5 January 2017


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 example 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
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.

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 an If-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 for 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):

Variable A :=
Index_1 ▶
1 A, B, C
2 D, E, F
3 G, H, I
Index_2 ▶
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_2 ▶
Index_1 ▼ 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.

Tip
For the special case of a dot product, for an expression of the form 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 value 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 value 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.

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

Iterate (initial, expr, until, maxIter, warnFlag)

Suppose the definition of variable x contains a call to Iterate(). Iterate() initializes x to the value of «initial». While stopping condition «until» is False (zero), it evaluates expression «expr», and assigns the result to x. Given the optional parameter «maxIter», it stops after «maxIter» iterations and, if «warnFlag» is True, issues a warning — unless it has already been stopped by «until» becoming True. If «until» is array-valued, it only stops when all elements of «until» are True.

Iterate() is designed for convergence algorithms where an expression must be recomputed an unknown number of iterations. Iterate (like Dynamic) must be the main expression in a definition — it cannot be nested within another expression. But it can, and usually does, contain nested expressions as some of its parameters. Iterate() (again like Dynamic() and unlike other functions) can, and usually does, mention the variable x that it defines within the expressions for initial and until. These expressions can also refer to variables that depend on x.

If you use Iterate() in more than one node in your model, you should be careful that the two functions don’t interact adversely. In general, two nodes containing Iterate() should never be mutual ancestors of each other. Doing so makes the nesting order ambiguous and can result in inconsistent computations. Likewise, care must be taken to avoid similar ambiguities when using interacting Iterate and Dynamic loops.

Tip
You can usually write convergence algorithms more cleanly using While. One difference is that While requires its stopping condition «Test» to be an atom, where Iterate() allows an array-valued stopping condition «until». Nevertheless, it is usually better to use While because you want it to do an appropriate number of iterations for each element of «until», rather than continue until all its elements are True. But, with While you need to use one of the tricks described in While and array abstraction to ensure the expression fully supports array abstraction.

See Also


Comments


You are not allowed to post comments.