Difference between revisions of "Var..Do"
(Recommendation to use Local instead.) |
(Use of Local..Do for declarations) |
||
Line 3: | Line 3: | ||
{{ReleaseBar}} | {{ReleaseBar}} | ||
− | {{Release|5.0||'' | + | {{Release|5.0||''The <code>Var</code> is now deprecated. We recommend you use [[Local|Local..Do]] instead.''}} |
− | == | + | == {{Local}} ''x'' := ''expr'' Do ''body'' == |
Declares a local value with identifier «x», so that the identifier «x» refers to the value obtained by evaluating «expr». The identifier «x» can then be referred to within the «body» expression. The expression «body» is said to be the ''lexical context'' of «x», since outside of the lexical context, the identifier «x» is not recognized. | Declares a local value with identifier «x», so that the identifier «x» refers to the value obtained by evaluating «expr». The identifier «x» can then be referred to within the «body» expression. The expression «body» is said to be the ''lexical context'' of «x», since outside of the lexical context, the identifier «x» is not recognized. | ||
− | + | {{Local}} is often used in a procedural syntax, where the declaration is followed by a semi-colon and the '''Do''' keyword is omitted, such as: | |
− | :<code> | + | :<code>{{Local}} x := [[Sum]](A, I);</code> |
− | :<code> | + | :<code>{{Local}} y := [[Sum]](B, I);</code> |
:<code> (x + y) * (x - y)</code> | :<code> (x + y) * (x - y)</code> | ||
With this syntax, the lexical context for «x» extends from the expression immediately following the semi-colon to the end of the sub-expression that the [[Var..Do]] declaration is embedded in. For example, in the following expression the lexical scope of a is shown in green. | With this syntax, the lexical context for «x» extends from the expression immediately following the semi-colon to the end of the sub-expression that the [[Var..Do]] declaration is embedded in. For example, in the following expression the lexical scope of a is shown in green. | ||
− | :<code>1 + ( | + | :<code>1 + ({{Local}} a := b^2; <font color = 'green'>{{Local}} c := a/b; c^2 - a - c - 2</font>) + 5</code> |
== Dimensionality Declaration == | == Dimensionality Declaration == | ||
The ''allowed'' dimensions of a local value can be declared using the syntax: | The ''allowed'' dimensions of a local value can be declared using the syntax: | ||
− | :<code> | + | :<code>{{Local}} «x»[«indexList»] := «expr» Do «body»</code> |
an equivalent anachronism (considered deprecated) is | an equivalent anachronism (considered deprecated) is | ||
− | :<code> | + | :<code>{{Local}} «x» := «expr» in each «indexList» Do «body»</code> |
There are some situations where the extra information about which indexes are allowed is required in order to ensure that the «body» expression will array abstract correctly when new dimensions are added to a model later. | There are some situations where the extra information about which indexes are allowed is required in order to ensure that the «body» expression will array abstract correctly when new dimensions are added to a model later. | ||
Line 34: | Line 34: | ||
The following computes the standard deviation across only the time periods that are profitable: | The following computes the standard deviation across only the time periods that are profitable: | ||
− | :<code> | + | :<code>{{Local}} earnings[Time] := revenue-expenses;</code> |
− | :<code> | + | :<code>{{LocalIndex}} profitTimes := Subset(earnings > 0);</code> |
− | :<code>SDeviation(earnings[Time = profitTimes], profitTimes)</code> | + | :<code>[[SDeviation]](earnings[Time = profitTimes], profitTimes)</code> |
Without the dimensional declaration restricting <code>earnings</code> to the [[Time]] index, [[Subset]] would complain that earnings has more than dimension in the event that <code>revenue-expenses</code> has an index in addition to [[Time]]. The dimensional declaration here allows the expression to fully array abstract if new dimensions are added to the model. | Without the dimensional declaration restricting <code>earnings</code> to the [[Time]] index, [[Subset]] would complain that earnings has more than dimension in the event that <code>revenue-expenses</code> has an index in addition to [[Time]]. The dimensional declaration here allows the expression to fully array abstract if new dimensions are added to the model. | ||
The above expression is meant to be illustrative, but for completeness we also note an alternative expression for the same computation that does not require iteration: | The above expression is meant to be illustrative, but for completeness we also note an alternative expression for the same computation that does not require iteration: | ||
− | :<code> | + | :<code>{{Local}} earnings := revenue - expenses Do [[SDeviation]](earnings, Time, w: earnings > 0)</code> |
=== Atomic Declarations === | === Atomic Declarations === | ||
A special case of the dimensional declaration is the declaration that a local value must be ''atomic'' -- i.e., a single non-array value. In this case, the we simply specify a zero-length list of allowed indexes: | A special case of the dimensional declaration is the declaration that a local value must be ''atomic'' -- i.e., a single non-array value. In this case, the we simply specify a zero-length list of allowed indexes: | ||
− | :<code> | + | :<code>{{Local}} «x»[] := «expr» Do «body»</code> |
Then inside «body», «x» is guaranteed to be atomic. | Then inside «body», «x» is guaranteed to be atomic. | ||
Line 51: | Line 51: | ||
==== Example ==== | ==== Example ==== | ||
The following computes the log-factorial of a number in an [[Array Abstraction|array-abstractable]] fashion (i.e., works even if ''n'' is originally an array: | The following computes the log-factorial of a number in an [[Array Abstraction|array-abstractable]] fashion (i.e., works even if ''n'' is originally an array: | ||
− | :<code> | + | :<code>{{Local}} n[] := n do [[Sum]]([[Ln]](1..n))</code> |
Note: The local value can have the same identifier as a global variable, and the value of the global can appear within «expr» since that is outside the local identifier's lexical scope. Inside «body», the identifier always refers to the local value. Having two local values with the same identifier is not allowed. | Note: The local value can have the same identifier as a global variable, and the value of the global can appear within «expr» since that is outside the local identifier's lexical scope. Inside «body», the identifier always refers to the local value. Having two local values with the same identifier is not allowed. | ||
Line 59: | Line 59: | ||
:<code>Atomic «x» := «expr» Do «body»</code> | :<code>Atomic «x» := «expr» Do «body»</code> | ||
− | This syntax is equivalent to <code> | + | This syntax is equivalent to <code>{{Local}} «x»[] := «expr» Do «body»</code>{{Release||4.6| as long as the result of «expr» does not contain any [[Handle]]s. It is actually equivalent to the following variation of [[Var..Do]]: |
− | :<code>MetaVar «x»[] := «expr» Do «body»</code> | + | :<code>MetaVar «x»[] := «expr» Do «body»</code>}} |
== Explicit Iteration == | == Explicit Iteration == | ||
The following syntax: | The following syntax: | ||
− | :<code> | + | :<code>{{Local}} «x» := «expr» In «I» Do «body»</code> |
− | evaluates «expr», then iterates over each element of index «I», setting «x» to the «expr»[«I» = i] slice while «body» is evaluated. In a sense, this is a dual to the dimension declaration -- here we are specifying the dimensions that are ''not'' allowed in «x», while the '' | + | evaluates «expr», then iterates over each element of index «I», setting «x» to the «expr»[«I» = i] slice while «body» is evaluated. In a sense, this is a dual to the dimension declaration -- here we are specifying the dimensions that are ''not'' allowed in «x», while the ''{{Local}} «x»[«I»] := ...'' syntax specifies the dimensions that are allowed. However, in this syntax, only a single index can be specified. |
== Assignment == | == Assignment == | ||
Although side-effects are generally prohibited from within Analytica expressions (due to dependency-maintenance and Analytica's adherence to the principle of [http://en.wikipedia.org/wiki/index.php/Referential_transparency_(computer_science) referential transparency]), you can change the value of a local value using the [[Assignment Operator::_::=|assignment operator, :=]]. For example: | Although side-effects are generally prohibited from within Analytica expressions (due to dependency-maintenance and Analytica's adherence to the principle of [http://en.wikipedia.org/wiki/index.php/Referential_transparency_(computer_science) referential transparency]), you can change the value of a local value using the [[Assignment Operator::_::=|assignment operator, :=]]. For example: | ||
− | :<code> | + | :<code>{{Local}} n := 27;</code> |
− | :<code> | + | :<code>{{Local}} steps := 0;</code> |
− | :<code>While (n > 2) Do (</code> | + | :<code>[[While]] (n > 2) Do (</code> |
::<code>steps := steps + 1;</code> | ::<code>steps := steps + 1;</code> | ||
− | ::<code>n := | + | ::<code>n := [[If]] [[Mod]](n, 2) [[Then]] n/2 [[Else]] 3*n + 1</code> |
:<code>);</code> | :<code>);</code> | ||
:<code>steps</code> | :<code>steps</code> | ||
Line 85: | Line 85: | ||
== Evaluation Mode == | == Evaluation Mode == | ||
− | A local value refers to a value, not an object. Hence, the terminology "local value" should be used and it should not be called a "local variable". A local value is not a variable -- a variable in an object that has attributes, has a separate mid-value and sample-value, and usually appears on an influence diagram. A local value has no attributes, does not appear in the global namespace, and does not maintain a separate [[Mid]]- and [[Sample]]-value. | + | A local value refers to a value, not an object. Hence, the terminology "local value" (or just "local") should be used and it should not be called a "local variable". A local value is not a variable -- a variable in an object that has attributes, has a separate mid-value and sample-value, and usually appears on an influence diagram. A local value has no attributes, does not appear in the global namespace, and does not maintain a separate [[Mid]]- and [[Sample]]-value. |
When the local value is declared, «expr» is evaluated in the current Evaluation mode. From that point on, «x» becomes an alias for the value that resulted from that evaluation, whether or not the identifier «x» appears in Mid- or Sample- context. This can be a source of confusion. Consider the following example: | When the local value is declared, «expr» is evaluated in the current Evaluation mode. From that point on, «x» becomes an alias for the value that resulted from that evaluation, whether or not the identifier «x» appears in Mid- or Sample- context. This can be a source of confusion. Consider the following example: | ||
− | :<code> | + | :<code>{{Local}} u := [[Uniform]](0, 1);</code> |
:<code>[[SDeviation]](u)</code> | :<code>[[SDeviation]](u)</code> | ||
− | When this expression is evaluated in [[Mid]] mode it is not equivalent to <code>SDeviation(Uniform(0, 1))</code>. The later evaluates to 0.29, while the former results in 0. This is because <code>u</code> is assigned <code>Mid(Uniform(0,1))</code>, which is 0.5, and then the result is <code>SDeviation(0.5)</code>, which is zero. | + | When this expression is evaluated in [[Mid]] mode it is not equivalent to <code>[[SDeviation]]([[Uniform]](0, 1))</code>. The later evaluates to 0.29, while the former results in 0. This is because <code>u</code> is assigned <code>[[Mid]]([[Uniform]](0,1))</code>, which is 0.5, and then the result is <code>[[SDeviation]](0.5)</code>, which is zero. |
You can, of course, call [[Sample]]() or [[Mid]]() explicitly from «expr» when desired, e.g.: | You can, of course, call [[Sample]]() or [[Mid]]() explicitly from «expr» when desired, e.g.: | ||
− | :<code> | + | :<code>{{Local}} u := [[Sample]]([[Uniform]](0, 1));</code> |
:<code>[[SDeviation]](u)</code> | :<code>[[SDeviation]](u)</code> | ||
Line 101: | Line 101: | ||
== Meta-Inference and the use of handles == | == Meta-Inference and the use of handles == | ||
− | Most models built in Analytica make no use of [[handle]]s, and so the considerations described here impact only the most advanced modelers. Inference involving [[handle]]s provides a mechanism for [[meta-Inference]] -- that is, reasoning about or altering your model from within Analytica itself. Advanced uses of [[meta-Inference]] can be used to extend Analytica's capabilities in many ways, creating functionality in your model beyond what is offered directly by the Analytica interface. | + | {{Release||4.6|Most models built in Analytica make no use of [[handle]]s, and so the considerations described here impact only the most advanced modelers. Inference involving [[handle]]s provides a mechanism for [[meta-Inference]] -- that is, reasoning about or altering your model from within Analytica itself. Advanced uses of [[meta-Inference]] can be used to extend Analytica's capabilities in many ways, creating functionality in your model beyond what is offered directly by the Analytica interface. |
A [[handle]] is essentially a pointer to an Analytica object, such as a Variable, Index, or Module object. [[Meta-inference]] implementations usually need to store [[handle]]s inside local values, assign handles to local values, read information about the objects pointed to by these handles, and manipulate the objects pointed to by these handles. | A [[handle]] is essentially a pointer to an Analytica object, such as a Variable, Index, or Module object. [[Meta-inference]] implementations usually need to store [[handle]]s inside local values, assign handles to local values, read information about the objects pointed to by these handles, and manipulate the objects pointed to by these handles. | ||
Line 107: | Line 107: | ||
When implementing [[meta-inference]] algorithms, two variations of [[Var..Do]] are preferred when [[Handles]] are involved, since these two variations have a cleaner semantics. They are: | When implementing [[meta-inference]] algorithms, two variations of [[Var..Do]] are preferred when [[Handles]] are involved, since these two variations have a cleaner semantics. They are: | ||
− | * LocalAlias «x» := «expr» Do «body» | + | * [[LocalAlias]] «x» := «expr» Do «body» |
*: or equivalently, Alias «x» := «expr» Do «body» | *: or equivalently, Alias «x» := «expr» Do «body» | ||
* MetaVar «x»''[«indexList»]'' := «expr» Do «body» | * MetaVar «x»''[«indexList»]'' := «expr» Do «body» | ||
Line 126: | Line 126: | ||
:<code>Var x := [[Handle]](A, asIndex: true);</code> | :<code>Var x := [[Handle]](A, asIndex: true);</code> | ||
:<code>x := x + 1</code> | :<code>x := x + 1</code> | ||
+ | }}{{Release|5.0||When you assign a handle to a local identifier that has been declared using {{Local}}, the value itself has a data type of [[Handle]]. This can be contrasted with locals declared using {{LocalAlias}}, for which the identifier is the identifier of the object. Consider this example | ||
+ | |||
+ | <code> | ||
+ | :{{Local}} h := [[Handle]](Va1); | ||
+ | :h := 2 | ||
+ | :{{LocalAlias x := [[Handle]](Va3); | ||
+ | :x = 3 | ||
+ | </code> | ||
+ | |||
+ | After this code is evaluated, the object <code>Va1</code> remains unchanged, the local <code>h</code> now has a value of 2 instead of a handle, and the [[Definition]] of the global variable <code>Va2</code> has been changed to 3. The local <code>x</code> still refers to the same variable as the global identifier <code>Va3</code>. The last line is only allowed in a context that allows side-effects to global variables, such as from a button script. | ||
+ | |||
+ | When you have a handle to an object in a {{Local}}, and you want to use it as if it were a global variable, you do this by using {{LocalAlias}} as illustrated here | ||
+ | <code> | ||
+ | :{{Local}} h := [[Handle]]( I ); { The local with the handle to an object, in this case an index } | ||
+ | :{{LocalAlias}} J := h; | ||
+ | [[Sum]]( a, J ) | ||
+ | }} | ||
+ | |||
+ | == History == | ||
+ | There has been a long history of changes and enhancements to the Analytica language's syntax and treatment of local variables, which has lead to a proliferation of different constructs for declaring local variables, with subtle distinctions between them, and many of the distinctions really just required to support backward compatibility for models created in older releases of Analytica. Starting with [[Analytica 5.0]], this complexity has been simplified, so that now there are only three ways you need to know for declaring locals: | ||
+ | <code> | ||
+ | * [[Local]] v := ...; | ||
+ | * [[LocalAlias]] x := ...; | ||
+ | * [[LocalIndex]] I := ... | ||
+ | </code> | ||
+ | In addition, you may want to use [[For..Do]] in some occasions, which also introduced a local identifier. | ||
+ | |||
+ | The [[Local]] keyword was introduced in [[Analytica 5.0]] as is equivalent to [[MetaVar..Do]]. [[MetaVar..Do]] was introduced (in release [fill in]) to correct inconsistencies in [[Var..Do]]. [[Var..Do]] still has those inconsistencies, which can make it confusing to use when writing code that involves handles, but it continued to be used extensively since [[MetaVar..Do]] had such an unnatural name. In Analytica 1.0, local variables were declared via a [[Using..Do]] construct, now considered very archaic and seldom seen. | ||
== See Also == | == See Also == |
Revision as of 15:37, 7 June 2018
Release: |
4.6 • 5.0 • 5.1 • 5.2 • 5.3 • 5.4 • 6.0 • 6.1 • 6.2 • 6.3 • 6.4 • 6.5 |
---|
The Var
is now deprecated. We recommend you use Local..Do instead.
Local x := expr Do body
Declares a local value with identifier «x», so that the identifier «x» refers to the value obtained by evaluating «expr». The identifier «x» can then be referred to within the «body» expression. The expression «body» is said to be the lexical context of «x», since outside of the lexical context, the identifier «x» is not recognized.
Local is often used in a procedural syntax, where the declaration is followed by a semi-colon and the Do keyword is omitted, such as:
With this syntax, the lexical context for «x» extends from the expression immediately following the semi-colon to the end of the sub-expression that the Var..Do declaration is embedded in. For example, in the following expression the lexical scope of a is shown in green.
Dimensionality Declaration
The allowed dimensions of a local value can be declared using the syntax:
Local «x»[«indexList»] := «expr» Do «body»
an equivalent anachronism (considered deprecated) is
Local «x» := «expr» in each «indexList» Do «body»
There are some situations where the extra information about which indexes are allowed is required in order to ensure that the «body» expression will array abstract correctly when new dimensions are added to a model later.
When the allowed indexes are declared, Analytica will ensure that when «body» is evaluated, the value of «x» will not have any indexes not listed in «indexList». If the original value assigned to «x» has indexes beyond those found in «indexList», Analytica will automatically iterate, evaluating «body» multiple times one slice at a time.
If the result of «expr» does not already have all the indexes declared in «indexList», the missing indexes are NOT added to «x».
Example
The following computes the standard deviation across only the time periods that are profitable:
Local earnings[Time] := revenue-expenses;
LocalIndex profitTimes := Subset(earnings > 0);
SDeviation(earnings[Time = profitTimes], profitTimes)
Without the dimensional declaration restricting earnings
to the Time index, Subset would complain that earnings has more than dimension in the event that revenue-expenses
has an index in addition to Time. The dimensional declaration here allows the expression to fully array abstract if new dimensions are added to the model.
The above expression is meant to be illustrative, but for completeness we also note an alternative expression for the same computation that does not require iteration:
Local earnings := revenue - expenses Do SDeviation(earnings, Time, w: earnings > 0)
Atomic Declarations
A special case of the dimensional declaration is the declaration that a local value must be atomic -- i.e., a single non-array value. In this case, the we simply specify a zero-length list of allowed indexes:
Local «x»[] := «expr» Do «body»
Then inside «body», «x» is guaranteed to be atomic.
Example
The following computes the log-factorial of a number in an array-abstractable fashion (i.e., works even if n is originally an array:
Note: The local value can have the same identifier as a global variable, and the value of the global can appear within «expr» since that is outside the local identifier's lexical scope. Inside «body», the identifier always refers to the local value. Having two local values with the same identifier is not allowed.
Atomic..Do syntax
Analytica also recognizes the following syntax for declaring a local value as atomic:
Atomic «x» := «expr» Do «body»
This syntax is equivalent to Local «x»[] := «expr» Do «body»
Explicit Iteration
The following syntax:
Local «x» := «expr» In «I» Do «body»
evaluates «expr», then iterates over each element of index «I», setting «x» to the «expr»[«I» = i] slice while «body» is evaluated. In a sense, this is a dual to the dimension declaration -- here we are specifying the dimensions that are not allowed in «x», while the Local «x»[«I»] := ... syntax specifies the dimensions that are allowed. However, in this syntax, only a single index can be specified.
Assignment
Although side-effects are generally prohibited from within Analytica expressions (due to dependency-maintenance and Analytica's adherence to the principle of referential transparency), you can change the value of a local value using the assignment operator, :=. For example:
Assignment always resets the value of «x», even if «x» contains a handle. In other words, when you assign to a local value, you are resetting the value that the local identifier refers to, as opposed to changing the value of the object pointed to by the local value. See more in the section below on Meta-Inference.
Slice assignment
You can also assign to individual slices of a local value. This is described in detail at Slice assignment.
Evaluation Mode
A local value refers to a value, not an object. Hence, the terminology "local value" (or just "local") should be used and it should not be called a "local variable". A local value is not a variable -- a variable in an object that has attributes, has a separate mid-value and sample-value, and usually appears on an influence diagram. A local value has no attributes, does not appear in the global namespace, and does not maintain a separate Mid- and Sample-value.
When the local value is declared, «expr» is evaluated in the current Evaluation mode. From that point on, «x» becomes an alias for the value that resulted from that evaluation, whether or not the identifier «x» appears in Mid- or Sample- context. This can be a source of confusion. Consider the following example:
Local u := Uniform(0, 1);
SDeviation(u)
When this expression is evaluated in Mid mode it is not equivalent to SDeviation(Uniform(0, 1))
. The later evaluates to 0.29, while the former results in 0. This is because u
is assigned Mid(Uniform(0,1))
, which is 0.5, and then the result is SDeviation(0.5)
, which is zero.
You can, of course, call Sample() or Mid() explicitly from «expr» when desired, e.g.:
Local u := Sample(Uniform(0, 1));
SDeviation(u)
This confusion can be avoided by adhering to and conceptualizing the terminology that «u» is a local value, not a local variable.
Meta-Inference and the use of handles
{{Release|5.0||When you assign a handle to a local identifier that has been declared using Local, the value itself has a data type of Handle. This can be contrasted with locals declared using Template:LocalAlias, for which the identifier is the identifier of the object. Consider this example
After this code is evaluated, the object Va1
remains unchanged, the local h
now has a value of 2 instead of a handle, and the Definition of the global variable Va2
has been changed to 3. The local x
still refers to the same variable as the global identifier Va3
. The last line is only allowed in a context that allows side-effects to global variables, such as from a button script.
When you have a handle to an object in a Local, and you want to use it as if it were a global variable, you do this by using Template:LocalAlias as illustrated here
- Local h := Handle( I ); { The local with the handle to an object, in this case an index }
- Template:LocalAlias J := h;
Sum( a, J )
}}
History
There has been a long history of changes and enhancements to the Analytica language's syntax and treatment of local variables, which has lead to a proliferation of different constructs for declaring local variables, with subtle distinctions between them, and many of the distinctions really just required to support backward compatibility for models created in older releases of Analytica. Starting with Analytica 5.0, this complexity has been simplified, so that now there are only three ways you need to know for declaring locals:
- Local v := ...;
- LocalAlias x := ...;
- LocalIndex I := ...
In addition, you may want to use For..Do in some occasions, which also introduced a local identifier.
The Local keyword was introduced in Analytica 5.0 as is equivalent to MetaVar..Do. MetaVar..Do was introduced (in release [fill in]) to correct inconsistencies in Var..Do. Var..Do still has those inconsistencies, which can make it confusing to use when writing code that involves handles, but it continued to be used extensively since MetaVar..Do had such an unnatural name. In Analytica 1.0, local variables were declared via a Using..Do construct, now considered very archaic and seldom seen.
See Also
Enable comment auto-refresher