Assignment Operator :=


Syntax

v := expr

Description

Evaluates the expression on the right-hand side, assigns the resulting value to v, and returns the resulting value.

Use of assignment is substantially restricted in Analytica. Assignment, by definition, has a side-effect of changing v. Side-effects are largely inconsistent with key features of Analytica, especially dependency maintenance, the declarative semantics of variable definitions, and the functional expression language. Therefore, assignment is only allowed situations that are not considered to be in conflict with features.

Assignment may be used in the following cases:

  • When v is a local variable, defined using Var..Do
  • When the assignment expression occurs in a button script.
  • When the assignment occurs in a user-defined function called from a button script.
  • When the assignment is to the RandomSeed system variable.

In addition to these caveats, even if assignment is legal, it may produce results that are counter-intuitive to people accustomed to other procedural programming languages. This is particularly the case when the assignment operator is used within the THEN or ELSE clause of a conditional IF-THEN-ELSE.

Assignment to Local Variables

The following example demonstrates the use of a local variable in an expression:

var count := 0;
for j:=I do (
  count := count + (A[I=j] <> Null)
);
count

This expression adds up the elements of A along I that are not equal to NULL. Note that if A has dimensions other than I, this expression works fine, and count will contain these other indexes. In general, to create flexible expressions, it is a good idea to think about whether your use of assignment will generalize when new dimensions are added.

Use in a Button Script

Assignment can be used within a button script to set the definition (or other attribute) of any object in your model. Assignment to global objects is only allowed from a button script, from a function called from a button script, or from typescript. It is not allowed when a variable that participates in dependency maintenance is being evaluated.

When called directly from the script attribute of a button or pict object, it is necessary to surround an assignment operation in parentheses, such as:

( Va1 := expr )

Without parentheses, a script will interpret the expression as typescript rather than as an expression. When evaluated in typescript, the right-hand side is not evaluated, so the definition of Va1 would be set literally to the character-for-character expression written on the right-hand side. The parentheses cause typescript to interpret the line as an expression.

To avoid this subtlety, we often recommend creating any complex button-script logic in a user-defined function, where the logic resides in a definition using the syntax most Analytica users are already well-accustomed to. Your button script can then consist of a simple single call to your user-defined function.

When assigning to a global object, the right-hand side is evaluated, and the definition of the object set to the literal value if atomic, or to an edit table containing the literal values that were computed. There are many uses for this, not covered here. Assignment in this fashion should not be used when the result of evaluating the right-hand side is dimensioned by a local index. (Edit tables cannot be defined over local indexes). If you have a local index, you should set up a global index first, and redimension the result before assigning it to the variable.

Assignment to Attributes

The assignment operator can also be used to assign to any attribute of a global object when used directly or indirectly from a button script. Here the syntax is

 attrib of obj := expr

An example is

 description of X := TextSentenceCase( TextReplace( Template, "%1%", "X") )

The right-hand side is evaluated, and the attribute is set to the value computed.

When used directly from a script, again parentheses are required, so that it is interpreted as an expression.

Note that the following two expressions are not equivalent

X := "B"
definition of X := "B"

In the first case, the definition of X will contain the quotation characters, and when evaluated, X will be the text value "B". In the second case, the definition of X will not contain quotation characters, and when evaluated, it will return the result of evaluating the variable B. The attrib of obj syntax does not set up edit-table definitions.

The only way to remove an attribute value from a script is using:

attrib of X := Undefined

The related expression

attrib of X := Null

sets the attribute value to Null, which is a legitimate attribute value and does not have same effect within Analytica (internally these have different meanings). However, from an Analytica expression, it is not possible to differentiate between an attribute value that does not exist and an attribute value set to Null. (While this distinction was possible prior to 4.0, it has been removed to simplify things for users). This method for removing an attribute value is the only instance where a user may need to be aware of the distinction between Undefined and Null.

Assignment to RandomSeed

When Analytica uses pseudo-random numbers, such as when sampling from a distribution, the actual sample generated is based on the current RandomSeed, used by the internal random number generators. If you evaluate an uncertain variable at different times, or chance variables in different orders, you are likely to get different samples.

To reproduce the same sample each time, one method that can be used is to reset the random seed to a known value prior to calling the distribution function. For example,

 RandomSeed := 999;  Normal(0,1)

This would generate the same sample for the normal distribution same every time it is evaluated. Analytica allows an assignment to the RandomSeed system variable from any expression, and doing so from a variable or function definition does not cause previously computed samples or results to be invalidated. However, this is the only global object that can be assigned to while a variable is being evaluated.

Gotchas -- Interactions of Assignment with Array Abstraction

Analytica performs array-based operations. When an operation has side-effects, as the assignment operator does, this creates some highly counter-intuitive situations. For example, compare the following three expressions, all of which may seem the same to someone accustomed to a procedural programming language, but for which only (C) produces the expected result

(A)
var result := Uniform(0,1);
if result<0.5 then result := 0.5;
result
(B)
var result := Uniform(0,1);
if result<0.5 then result:=0.5 else result:=result
(C)
var result := Uniform(0,1);
result := if result<0.5 then 0.5 else result

To understand this foible, you must realize that Analytica evaluates the IF-THEN-ELSE in an array fashion, evaluating the entire IF part, then evaluating the THEN part in an array-operation only once, and evaluating the ELSE part in an array fashion only once. It is not iterating over each atomic element of result. (We sometimes refer to this distinction has "vertical" vs. "horizontal" abstraction).

In case (A), when result<0.5 is evaluated (assuming Sample mode), at least one element of result is likely to satisfy result<0.5, so the THEN clause will be evaluated. When this happens, the local variable is set to the scalar value of 0.5 -- it is no longer indexed by Run. Probably not what the author expected.

In case (B) result<0.5 will have some true and some false instances, so both the Then and Else clauses will be evaluated. When Then is evaluated, the entire value becomes 0.5 as the assignment side-effect, and the result:=result part has no real impact. Again, not what the author expected.

Case (C) does work as expected. Here the if-then-else is evalutaed as an array operation, returning the correct truncating, and the assignment of the entire array occurs once.

As a general rule, to avoid this foible, you should avoid using assignment from within a conditional THEN or ELSE clause. There are situations where you can legitimately do so, but when doing so, you should ensure that your antecedent (the IF condition) is guaranteed to be a scalar at all times.

Slice/Subscript Assignment

(new to 4.0)

When v is a local variable, you may assign to a single slice of a value, leaving all other current values of the local variable unchanged. The syntax for this is:

v[I=x]  := y
v[@I=n] := x 

This is only permitted when v is a local variable.

If you wish to change a single slice of a global variable, X, from a button script, and if you can guarantee that every cell of the global variable contains a literal value, you can accomplish this using:

var v := Va1;
v[I=x] := y;
Va1 := v

You could also accomplish this equivalently using:

Va1 := if I=x then y else Va1

For a single slice, the latter is equally efficient; however, if you have a complex algorithm that will manipulate many slices and perform many slice assignments in the process, direct assignment to slices prior to writing the value back to the global value is substantially more efficient.

Additional information on Slice Assignment is available at Subscript/Slice Operator.

Assignment to Local Variables with Handles

This section is a work in progress, and at this time contains content that may apply only to Analytica 4.2. Until complete, there may be errors, please bear with me.

There are several different constructs for declaring local variables, resulting in several nuances in behavior with regard to assignment. For most users of Analytica, these nuances are unimportant -- their primary relevance is for meta-inference, where the distinctions with respect to how handles are processed becomes important. With respect to assignment, the key distinctions are what happens when you assign a value to a local variable that is current holding a handle, and whether the local variable is declared as an index or not.

In general, local variables are declared either with a declaration construct (e.g., Var..Do), or through a parameter declaration in a User-Defined Function.

When assignment is made to a local variable that currently contains a handle to another object, there are three distinct things that may happen, depending on which type of local variable is being used:

Meta-variable treatment
The local variable is changed, no longer pointing to the object, leaving the object unchanged, and causing the local variable to now contain the new value.
Alias treatment
The assignment applies to the object, changing its definition. This behavior may require the evaluation to be launched from a button script or require the object to be defined via the ComputedBy function.
Index treatment
The assignment creates a new local index object with the new value.

The following table shows how local variables are treated by assignment based on how they are declared:

How declared Treatment type by Assignment (x := ...) Notes
Local Declaration Constructs
Var..Do meta-variable
Using..Do meta-variable deprecated, identical to Var..Do
MetaVar..Do Meta-variable requires 4.2
LocalAlias..Do Alias requires 4.2
Index..Do Index (in 4.2+)

meta-var (in 4.1 & earlier)

MetaIndex..Do Index (in 4.2+)

meta-var (in 4.1 & earlier)

Function Parameter Declarations
x : Context (or none) Alias
x :Sample
x:Prob
x:Determ
etc.
Alias
x : Variable Alias (in 4.2+)

Meta-variable (in 4.1)

x : Object Alias (in 4.2+)

Meta-variable (in 4.1)

x : Index Index (in 4.2+)

meta-variable (in 4.1 & earlier)

x : Handle Meta-variable

When you are performing meta-inference, it is recommended that you limit yourself to the MetaVar..Do and LocalAlias..Do constructs for declaring local variables. The Var..Do construct has been around in Analytica for a long time, long before it was widely used for meta-inference and manipulation of handles. As a result of its legacy, and the need to continue supporting backward compatibility, its treatment of handles is a bit less consistent, which people have sometimes found to be a source of confusion. The LocalAlias..Do and MetaVar..Do variations were introduced for the purpose of providing two highly self-consistent, making their behavior easier to fully understand. Again, the distinctions only have a bearing when you are manipulating handles to other objects.

Examples

Var x := Handle(Va1);

x := 6

local x becomes 6.

Va1 unchanged

MetaVar x:=Handle(Va1);

x := 6

local x becomes 6.

Va1 unchanged

LocalAlias x:=Handle(Va1);

x := 6

local x unchanged -- still points to Va1.

Va1's definition changed to 6

Index I := 1..5;

Var A := I^2;

I := 10..15

In Analytica 4.2:
A new local index I is created with an IndexValue of [10,11,12,13,14,15]].
The local I now refers to the new index.
Array A is indexed by the original index, A.I.

In Analytica 4.1:

I now refers to the list value [10,11,12,13,14,15]].
I no longer refers to an index object.
A is still indexed by the original local index, A.I.

See Also

Comments


You are not allowed to post comments.