Difference between revisions of "Handle Functions"
m (→Handle(v)) |
|||
Line 32: | Line 32: | ||
Variable Va1 := 5 | Variable Va1 := 5 | ||
− | Suppose you evaluate Fu1(Va1) and Fu2(Va1). Fu1 returns the value of Va1, that is 5. Fu2 returns a handle to the Va1 object. Hence, to return a Handle, a call to Function | + | Suppose you evaluate Fu1(Va1) and Fu2(Va1). Fu1 returns the value of Va1, that is 5. Fu2 returns a handle to the Va1 object. Hence, to return a Handle, a call to Function Handle was necessary. |
You can declare a local variable to be a Handle using, for example, | You can declare a local variable to be a Handle using, for example, |
Revision as of 17:46, 29 May 2009
What's new in Analytica 4.0? >
About Handles (varTerms)
A handle (formerly known as varTerm) is a reference or pointer to a variable, index, function, module, or other Analytica object. Using a handle lets you write variables or functions that works with the object itself rather than the value of the object. Usually, if you refer to variable X in an expression, say as:
Variable X := 100 Variable A := X
The value of A will be equal to the value of X, namely 100. But if you write:
Variable B := Handle(X)
B now refers to variable X, and you can now access the Title, Description, Inputs or Outputs of X. You can also create and use lists of handles to objects. For example, the list of indexes of an array, or the inputs or outputs of a variable.
If you define a variable as a list of variables:
Variable A := [X, Y, Z]
the value of A will be the result of evaluating X, Y, and Z. The index of A is a list of handles to X, Y, and Z. It usually shows the titles of X, Y, and Z. If you double-click the title (or identifier) of an object in the index in a Table window, it opens the Object view for that object. If "Show By Identifier" is on (from the Object menu, or if you toggle it with control-Y), the index shows identifiers rather than titles. Either way, you can double click to open the object view.
Some attributes consist of lists of handles, notably Inputs, Outputs, and Contains (the objects in a module). You can use these to write functions to find the ancestors, descendants, or contents of an object.
Handle(v)
Returns a handle (pointer or reference) to object v.
Function Handle( X : Variable ; AsIndex : optional boolean atomic )
If you want to create a user-defined function that returns a Handle, in most cases the return value needs to involve a call to the Handle function. If you simply place the identifier of a local variable (even if it declared as Variable) as the return value, it will be evaluated. For example, consider these two functions:
Function Fu1( X : Variable ) := X Function Fu2( X : Variable ) := Handle(X) Variable Va1 := 5
Suppose you evaluate Fu1(Va1) and Fu2(Va1). Fu1 returns the value of Va1, that is 5. Fu2 returns a handle to the Va1 object. Hence, to return a Handle, a call to Function Handle was necessary.
You can declare a local variable to be a Handle using, for example,
var a := Handle(Va1) do ...
where Va1 is the identifier of an object. In the scope of this var declaration, a then becomes an alias for Va1. In other words, anywhere you might use the identifier Va1 in an expression, you could equivalently use a.
To create an alias to an index, use
var I := Handle( In1, AsIndex:True ) do ...
The local variable I will then serve as an alias to the original index In1 inside the scope of this var statement. The AsIndex parameter causes I's index values to be used when I appears in a value context (this is relevant when the original object is a self-indexed array, having both a value and an index value).
When an index has handles as elements, and you wish to subscript across that index, the recommended syntax is:
A[ I = Handle(x) ]
In this example, x would be the identifier appearing as one of the elements of I.
Function HandleFromIdentifier
HandleFromIdentifier( varName : atomic text )
(formerly GetVariableByName).
Finds an object in the global namespace having the indicated name and returns a handle to that object. If no such object exists, returns Null (use IsUndef to test for null if you worry about backward compatibility).
One danger with using this function is that Analytica has no dependency influence information. For example, the following expression
var x := HandleFromIdentifier("Va1") do x
evaluates Va1 and returns that value; however, if Va1 were to change, Analytica would have no way of knowing that this result needs to be invalidated. Obviously, in this example, it would better to use just the identifier Va1 in the expression, but as a basis of comparison, note that Analytica would be aware of the dependency influence if this variation were used:
var x:= Handle(Va1) do x
If the value of Va1 changes, it would know that this value needs to be re-computed.
Handle to Caller of a UDF
(note: This does not need to be mentioned in the user-guide)
A special case, HandleFromIdentifier("_Caller"), can be used in a user-defined function to obtain a handle to the variable, function, or button that invoked the function. From a nested function, the variable or button that invoked the parent function is returned. The tag "_Caller" will not conflict with an identifier since identifiers may not begin with an underscore.
Using this, it is possible to create a function that bases its behavior based on a property of the variable that invokes it. Your function might, for example, use the value of a User-Defined Attribute (UDA) of the calling variable.
Function IndexesOf
IndexesOf( A : Array )
Returns a list of handles, each one serving as a handle to an index of array A.
This is similar to IndexNames(A), except that IndexesOf returns the handles of the indexes, while IndexNames returns the identifier of each index as a text value.
It is possible for an array to have more than one local index having the same identifier. Obviously, we don't recommend, but in the unusual case where this occurs, the handles returned by IndexesOf are unambiguous, where the results of IndexNames are ambiguous.
Local Variables and Handles
The value of a local variable, like a global variable, may be a number, text, or handle or an array or scalar. When a local variable is set to a single handle (not an array), there are two behaviors that are possible, depending on the type of local variable.
Starting with Analytica 4.2, there are essentially two (pure) types of local variables: MetaVars and LocalAliases. These behave quite differently when you assign them to be a single handle.
A MetaVar local contains whatever you assign it. Whenever you use its identifier in a value context, its identifier acts as the value assigned to it. So if you use a MetaVar identifier in a value context, its value is a handle to an object (not the value of the object pointed to). When you assign to a MetaVar, you are just changing its value -- the object previously pointed to is not impacted.
A LocalAlias local acts as an alias of another object when it is set to be a handle. Any use of a LocalAlias's identifier in an expression is equivalent to using the identifier of the object pointed to (at least for objects that exist in the global namespace). If you assign a new value to a LocalAlias-type local, you change the object pointed to, and the local remains an alias to that same variable (note: you are only allowed to change the value of another object when the assignment is evaluated from a button script).
When writing meta-inference algorithms, where your algorithm will be manipulating handles to objects, we recommend that you declare all your local variables using only MetaVar..Do or LocalAlias..Do depending on your intention, and avoid the use of Var..Do, or even For..Do. The recommendation is only for your own conceptual simplicity, since MetaVar and LocalAlias have such clean semantics, it is var less confusing. Note however that MetaVar..Do and LocalAlias..Do are new to Analytica 4.2.
When a local variable that is declared using Var..Do is assigned a handle, it acts as a hybrid of the MetaVar and LocalAlias semantics. In a value context, its identifier acts as an alias. However, when you assign to it, the local is changed to the new value without changing the object originally pointed to.
You may find yourself iterating over a list or array of handles, where you intend to operate on each handle in the list. Rather than use:
For v:=ListOfHandle Do «body»
it is better to use one of:
LocalAlias y := ListOfHandle Do «body» MetaVar z[] := ListOfHandle Do «body»
In each iteration of «body», y will be an alias of the object pointed to in the first form, and z will contain a handle to the object in the second form.
Function IsHandle(x)
The function IsHandle(X) tests whether the value contained in «X» is a handle. If the value of «X» is an array, then it tests whether each cell of the array contains a handle, and returns an array of 0s and 1s.
The call IsHandle(X,local:true), where «X» is a local variable, tests whether the local variable contains a handle. Note that if you don't include the local:true parameter, you are testing the value contained within the local, rather than the local itself (except in the case of a meta-local, in which case the test is valid even without the optional local:true). See IsHandle for details.
Display of Handles in Result Tables
When a handle object is displayed in a result table cell, the title or identifier of the object pointed to will be displayed, depending on whether the "Show by identifier" preference is on. Pressing CTRL-Y toggles this preference, and thus toggles the display between title and identifier.
Double clicking on a cell displaying a handle hyperlinks to the object pointed to. In Analytica 4.1, the hyperlink brings up the object window for the object. In Analytica 4.2, the default behavior jumps to the diagram containing the object, with the object node highlighted. In Analytica 4.2, you can control where the hyperlink jumps to by setting the preference attribute Att_hyperlinkPref. The attribute is inherited, so if you set it for a single variable, the setting controls the behavior only from that variable's result table. If you set it for a module, that defines the default behavior within that module, or if you set it for your top-level model object, it defines the default behavior model-wide. The attribute has three possible values:
- 1 = Jump to object's object window
- 2 = Jump to parent diagram containing object, with object selected (default)
- 3 = For handles to modules, open the module's diagram with nothing selected. For non-modules, jump to diagram containing object with object selected.
Enable comment auto-refresher