Difference between revisions of "For and While Loops"

(Created page with "Category: Analytica User Guide <breadcrumbs> Analytica User Guide > {{PAGENAME}}</breadcrumbs><br /> <Tip Title="Tip> Analytica’s Intelligent Array features means that...")
 
(Local Variables --> Local Values (terminology change))
 
(37 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
[[Category: Analytica User Guide]]
 
[[Category: Analytica User Guide]]
<breadcrumbs> Analytica User Guide > {{PAGENAME}}</breadcrumbs><br />
+
<breadcrumbs> Analytica User Guide > Procedural Programming > {{PAGENAME}}</breadcrumbs><br />
  
<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>
+
== 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 [[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 '''For''' loop successively assigns the next atom from array a to local index <code>i</code>, and evaluates expression '''expr'''. '''expr''' might refer to '''<code>i</code>''', for xample to slice out a particular element of an array. <code>a</code> might be a list of values defined by <code>m..n</code> or <code>Sequence(m, n, dx)</code> or it might be a multidimensional array. Normally, it evaluates the body <code>expr</code> once for each atom in <code>a</code>.  
+
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 <code>a</code> containing the values of each evaluation of <code>expr</code>. If any or all evaluations of <code>expr</code> 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.  
  
Usually, the Intelligent Array features take care of iterating over indexes of arrays without the need for explicit looping. A '''For''' loop is sometimes useful in these specialized cases:
+
<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>
  
* 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'''.
+
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 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.
+
* 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.
 
See below for an example of each of these three cases.
  
==== Library Special ====
+
==== Library ====
 +
Special
  
=====Avoiding out-of-range errors=====
+
====Avoiding out-of-range errors====
  
 
Consider the following expression:
 
Consider the following expression:
 +
:<code>If x < 0 Then 0 Else Sqrt(x)</code>
  
<code>If x<0 Then 0 Else Sqrt(x)</code>
+
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:
  
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 [http://wiki.analytica.com/index.php?title=Sqrt Sqrt](x) is evaluated before <code>If-Then-Else</code> selects which elements of [http://wiki.analytica.com/index.php?title=Sqrt 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>If x[i = j] < 0 then 0 else Sqrt(x[i = j])</code>
<code>For j:=I do</code>
 
 
 
<code>If x[i=j]<0 then 0 else Sqrt(x[i=j])</code>
 
  
 
Or as (see next section):
 
Or as (see next section):
  
<code>Using y:=x in i do</code>
+
:<code>Using y := x in i do</code>
 
+
::<code>If y < 0 Then 0 else Sqrt(y)</code>
<code>If y<0 Then 0 else Sqrt(y)</code>
 
 
 
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]
+
Situations like this can often occur during slicing operations. For example, to shift <code>x</code> one position to the right along <code>i</code>, the following expression would encounter an error:
  
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:
+
:<code>if i < 2 then x[i = 1] else x[i = i - 1]</code>
  
<code>For j:=i do
+
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:
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:
+
:<code>For j := i do
 +
::If j < 2 then x[i = 1] else x[i = j - 1]</code>
  
<code>Sqrt(if x<0 then 0 else x)</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:
  
<code>x[i=(if i<2 then 1 else i-1)]</code>
+
:<code>Sqrt(if x < 0 then 0 else x)</code>
 +
:<code>x[i = (if i < 2 then 1 else i - 1)]</code>
  
 
===== Dimensionality reduction =====
 
===== 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.
+
[[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 <code>A</code> indexed by <code>I</code>, and you wish to apply a function <code>f(x)</code> to each element of <code>A</code> along <code>I</code>. In a conventional programming language, this would require a loop over the elements of <code>A</code>; however, in almost all cases, Analytica’s array abstraction does this automatically — the expression is simply <code>f(A)</code>, and the result remains indexed by <code>i</code>. 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 <code>Atom</code>, as detailed in [[Parameter Qualifiers]]). For example, Analytica does not array abstract over functions such as [http://wiki.analytica.com/index.php?title=Sequence Sequence], [http://wiki.analytica.com/index.php?title=Split Split], [http://wiki.analytica.com/index.php?title=Subset Subset], or [http://wiki.analytica.com/index.php?title=Unique 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 <code>A</code> is an array of text values):
+
Suppose you have an array <code>A</code> indexed by <code>I</code>, and you wish to apply a function <code>f(x)</code> to each element of <code>A</code> along <code>I</code>. In a conventional programming language, this would require a loop over the elements of <code>A</code>; however, in almost all cases, Analytica’s array abstraction does this automatically — the expression is simply <code>f(A)</code>, and the result remains indexed by <code>i</code>. 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 <code>Atom</code>, 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 <code>A</code> is an array of text values):
  
:A: Index_1 ▼
+
:<code>Variable A :=</code>
 
:{| class="wikitable"
 
:{| class="wikitable"
|-
+
! colspan="3" style="text-align: left;" | Index_1 &#9654;
! <font color="#FFFFFF">1</font>
 
!
 
 
|-
 
|-
 
!1
 
!1
Line 73: Line 70:
 
|G, H, I
 
|G, H, I
 
|}
 
|}
<br />
 
  
:Index_1 ▼
 
 
:{| class="wikitable"
 
:{| class="wikitable"
 +
! colspan="3" style="text-align: left;" | Index_2 &#9654;
 
|-
 
|-
 
|1
 
|1
Line 83: Line 79:
 
|}
 
|}
  
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:
+
We wish to split the text values in <code>A</code> and obtain a two dimensional array of letters indexed by <code>Index_1</code> and <code>Index_2</code>. Since [[Split]] does not array abstract, we must do each row separately and re-index by <code>Index_2</code> before the result rows are recombined into a single array. This is accomplished by the following loop:
  
<code>FOR Row := Index_1 DO Array(Index_2, SplitText(A[Index_1=Row], ','))</code>
+
:<code>FOR Row := Index_1 DO Array(Index_2, SplitText(A[Index_1 = Row], ','))</code>
  
 
This results in:
 
This results in:
 
:Index_1 ▼, Index_2 ▶
 
 
 
:{| class="wikitable"
 
:{| class="wikitable"
 +
! !! colspan="3" | Index_2 &#9654;
 
|-
 
|-
!
+
! '''Index_1 &#9660; '''
 
! 1
 
! 1
 
! 2
 
! 2
Line 103: 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 118: Line 113:
 
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:
  
<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>Average(MatrixA*MatrixB, N)</code>
  
<code>MatrixB:</code> A two dimensional array indexed by N and P.
+
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.
 
 
<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.
 
  
 
To reduce the memory required, use the following expression instead:
 
To reduce the memory required, use the following expression instead:
  
<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 137: 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 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.
+
[[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 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 [[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) ¨ Null</code>
 +
 
 +
Using [[While]] often follows the following pattern:
 +
 
 +
:<code>Var x[]:= ...;</code>
 +
:<code>While (FunctionOf(x)) Do (</code>
 +
::<code>...</code>
 +
::<code>x := expr;</code>
 +
::<code>...</code>
 +
:<code>);</code>
 +
:<code>returnValue</code>
 +
 
 +
== Iterate (initial, expr, until, maxIter, ''warnFlag'') ==
 +
Suppose the definition of variable x contains a call to [[Iterate]](). [[Iterate]]() initializes <code>x</code> to the value of «initial». While stopping condition «until» is <code>False</code> (zero), it evaluates expression «expr», and assigns the result to <code>x</code>. Given the optional parameter «maxIter», it stops after «maxIter» iterations and, if «warnFlag» is <code>True</code>, issues a warning — unless it has already been stopped by «until» becoming <code>True</code>. If «until» is array-valued, it only stops when all elements of «until» are <code>True</code>.
 +
 
 +
[[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 <code>x</code>.
  
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
+
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.
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:
+
<Tip Title="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 <code>True</code>. But, with [[While]] you need to use one of the tricks described in [[Ensuring Array Abstraction|While and array abstraction]] to ensure the expression fully supports array abstraction.</Tip>
(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:
+
== See Also ==
 +
* [[Intelligent Arrays]]
 +
* [[For versus Var]]
 +
* [[Var..Do]]
 +
* [[For..Do]]
 +
* [[While..Do]]
 +
* [[Iterate]]
 +
* [[If-Then-Else]]
 +
* [[Looping over a model]]
 +
* [[Dynamic loop]]
  
Var x[]:= ...;
 
While (FunctionOf(x)) Do (
 
...
 
x := expr;
 
...
 
);
 
returnValue
 
  
<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.