Returning multiple values
Most Analytica expressions and functions, whether built-in or user-defined functions, return a single value -- which may be a single (scalar) or an array with one or multiple dimensions. But, it is possible and, sometimes very convenient, to return multiple values (which may each be a scalar or array with different dimensions.) For example, when reading a file, you can obtain the file name selected by the user as well as the contents:
If you only want the first (main) result, in this case the contents but not the filename, you can simply use:
Built-in functions that return multiple values
A number of built-in functions can return multiple values when their results is assigned to multiple corresponding local variables, as in the example above with ReadTextFile(). Similarly, they will return only their primary (first) result, when used in a simple expression or assigned to a single Local variable, if you don't need their other return values.
These functions that read from files allow the user to select the file from a file browser dialog. You can get the name of the file as their second return value. The first return value is the contents of the file.
- ReadTextFile: Returns ( file_contents, filename )
- ReadExportFile: Returns (array, filename )
- ReadBinaryFile: Returns (date, filename)
- ReadImageFile: Returns (image, filename)
- SpreadsheetOpen: Returns ( workbook, filename )
(Note: the Write functions don't yet return filenames in this fashion.)
- CanvasDrawText returns the coordinates of the bounding box of the text.
- Regression returns the bias coefficient as a second return value without having it included in the basis.
- MantissaAndExponent returns the mantissa and exponents of a number as separate return values.
- MsgBox (return, checkbox) As an option you can include a Checkbox in a dialog box displayed by this function. Use the multiple value return syntax to get both its primary return value and the state of the check box state.
These three important functions for regression and matrix computations return multiple values:
- Regression (basis, bias). When called to return two values, as in:
It includes the bias coefficient as the second return value. This means you can now find
b for simple y= m*x + b regression for scalar x using just a single call.
- SingularValueDecomp (u, w, v): Returns three matrices, each with different dimensions when called with multi-value syntax. If you call it with a single return value, it returns a vector of 3 references to the three returned matrices, for compatibility with earlier releases before multiple return values. But, you then would need to unpack the three values from the references. Now, it is more convenient to separate its returned values thus:
(u, w, v) := SingularValueDecomp( a, I, J, J2)
- EigenDecomp (eigenVals, eigenVecs), like SingularValueDecomp, can return its two results as separate values, when called as:
(eigenVals, eigenVecs) := EigenDecomp(a, I, J)
(Again for backward compatibility when called with a single result, it returns a vector of with references to the eigen values and 2-D matrix of eigen vectors.)
Syntax for multiple return values
There are two ways of doing capturing multiple return values from a function: As part of a local declaration:
Local( x, y, z ) := FuncWithMultiple( );
or in a simple assignment to multiple variables:
(a, b, c) := FuncWithMultiple( );
In the latter case, each destination variable (a, b and c) can be anything that could appear on the left-hand side of an normal assignment operator. This includes local identifiers, global variables (in contexts where a side-effect is legal), a slice of a local variable, or an attribute of an object.
Although I'm showing the local declaration example using Local..Do, you can also use other local declaration constructs in the same way, including For and LocalAlias, or the legacy Var..Do, Using..Do, MetaVar..Do, etc.
If you try to capture a return value from a function that doesn't have multiple return values, the extra variable(s) get null:
( x, y ) := Sqrt(5)
x to 5 and
y to null.
MultiResult: Multiple values from a user-defined function
You can define a user-defined function to return multiple values using this function in its definition:
MultiResult(v1, v2, v3)
v1, v2, … are variables or expressions computing the results is to be returned.
If the calling context does not actually capture all these values -- if the function is called without assigning it to multiple variables -- it will only evaluate the first v1 parameter, and not the rest. You can use that to your advantage by placing any time-consuming code not shared by v1 in the expression for v2, so it doesn't waste time evaluating v2 when the caller uses only v1.
There is a synonymous syntax,
_( v1, v2, v3, v4 ) that can be used. When returning values from a UDF, we feel that it clearer to call MultiResult. However, the underscore function is convenient when using a multiple assignment directly, such as
Local (key, val) := _( dict.Key, dict );
This example combines
_( ) with Repeated parameter forwarding to accomplish something that would have taken four lines of code otherwise.
Iteration with multiple values
Multiple return values enable convenient patterns with iteration constructs. For example, when iterating over an index, you can simultaneously grab the index position and the index labels in a FOR loop:
- For (pos, label) := _( @J, J ) Do …
A 1-D array with an index containing labels is called an associative array in other programming languages. The index values are the keys and the array values are the values. Your iteration can conveniently name both the current key and the current value.
- For ( key, val reduced ) := _( I, a ) Do …
Multiple return values and the associated extensions to syntax for declaration of local variables and assignment were introduced in Analytica 5.2