Local Indexes





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


You can declare a local index in the definition of a variable or function. It is possible that the value of the variable or value returned by the function is an array using this index. This is handy because it lets you define a variable or function that creates an array without relying on an externally defined index.

The construct, LocalIndex i := indexExpr defines an index «i» local to the definition in which it is used. The expression «indexExpr» can be a sequence, literal list, or other expression that generates an unindexed array, as used to define a global index. For example:

Variable PowersOf2 := LocalIndex j := 0..5; 2^j

The new variable PowersOf2 is an array of powers of two, indexed by the local index j, with values from 0 to 5:

PowersOf2 →
Result-powersof2.png

Dot operator: a . i

The dot operator in a.i lets you access a local index i via an array a that it dimensions. If a local index identifies a dimension of an array that becomes the value of a global variable, it can persist long after evaluation of the expression — unlike other local values which disappear after the expression is evaluated.

Even though local index j has no global identifier, you can access it via its parent variable with the dot operator (.), for example:

PowersOf2.j → [0, 1, 2, 3, 4, 5]

When using the subscript operation on a variable with a local index, you need to include the dot (.) operator, but do not need to repeat the name of the variable:

PowersOf2[.j = 5] → 32

Any other variables depending on PowersOf2 can inherit j as a local index — for example:

Variable P2 := PowersOf2/2
P2[.j = 5] → 16

Example using a local index

In this example, MatSqr is a user-defined function that returns the square of a matrix — i.e., A x A', where A' is the transpose of A. The result is a square matrix. Rather than require a third index as a parameter, MatSqr creates the local index, i2, as a copy of index i.

Function MatSqr(a: Array; i, j: Index)
Definition := LocalIndex i2:=CopyIndex(i); Sum(a*a[i = i2], j)

The local identifier, i2, in MatSqr is not within lexical scope in the definition of Z, so we must use the dot operator (.) to access this dimension. In the example below, we underline the dot operator for clarity:

Variable Z := Var XX := MatSqr(X, Rows, Cols);
Sum(XX * Y[I=XX.i2], XX.i2)

Analytica's arrays are, in general, multi-dimensional, where each dimension is identified with an Index. An Index is an Analytica object with a list of IndexValues. Indexes in Analytica come in three varieties:

  • Global Indexes: These appear as parallelagrams on a diagram, e.g., IndexNode.jpg
  • Self Indexes: A variable that can serve as an index, but also has a value. See Self-Indexed Arrays
  • Local Indexes: Index objects that do not exist in the global namespace.

Local indexes may be temporary, disappearing after they fall out of lexical context, or they may continue to exist outside of the lexical context where they were declared if an array indexed by the local index is returned.

Local indexes are declared within expressions via the LocalIndex..Do declaration, as in this example:

LocalIndex X := Sequence(x1, x2, (x2 - x1)/1000) Do ArgMax( f(X^2), X)

In the above example, the identifier X exists as a local identifier only within the body of the Do clause, and refers to the index defined as a sequence with 1000 points ranging from x1 to x2. ArgMax finds the maximum of f(X^2) and returns a single value, after which the index X ceases to exist.

Syntactically, an index can also be written using this syntax:

LocalIndex X := Sequence(x1, x2, (x2 - x1)/1000);
ArgMax(f(X^2), X)

Here the local identifier X is recognized to the end of the current lexical context.

It is possible for an expression to return an array that is indexed by a local index. When this happens, the local index continues to exist, although no local or global identifier refers to it directly. This happens, for example, in the following expression:

LocalIndex Bit := 7..0;
Mod(Floor(N/2^Bit), 2)

which returns the following when N := 137:

.Bit ▶
7 6 5 4 3 2 1 0
1 0 0 0 1 0 0 1

After this expression is evaluated, the local identifierBit no longer exists, but a 1-D array indexed by .Bit does exist. The only way to access this local index is through the array result. Suppose BinaryN is defined by the expression above. The syntax BinaryN.Bit identifies the index. So, for example, to count the number of bits:

Sum(BinaryN, BinaryN.Bit) → 3

where the second parameter to Sum expects an index.

To access the 3rd bit of BinaryN (counting from 0), we could write

BinaryN[BinaryN.Bit = 3]

or we can abbreviate in this case as

BinaryN[.Bit = 3]

since it is clear that .Bit is referring to an index of BinaryN. This abbreviation is only possible within the Subscript/Slice Operator, not in other contexts.

It is theoretically possible to create an array with more than one local index having the same name. In this case, the A.I syntax is ambiguous, and one index is identified.

Within an expression, if you have an array containing a local index, it is possible to define a local as an alias to refer to this index in order to avoid having to type, e.g., BinaryN.Bit every time. This is done using the Local..Do declaration, not the LocalIndex..Do, since the LocalIndex..Do construct would create an entirely new index object, and is done as follows:

Local I := Handle(BinaryN.Bit, asIndex: True) Do expr

where inside «expr», the local identifier «I» is an alias for the actual index object. Note that the local name used in «expr» does not have to be the same as the local index's object name. The optional «asIndex» parameter of Handle is not actually needed in the example here, but can optionally be included for clarity (it is necessary if you want to alias a self-index, as opposed to aliasing the object having a self-index). As a full example, the following expression bit-shifts the complement of BinaryN (divides the complement by 2), note how using I as an alias for BinaryN.Bit helps with conciseness within the expression:

Local I := Handle(BinaryN.Bit) Do
@I = 1 Or (Not BinaryN)[@I = @I - 1]
.Bit ▶
7 6 5 4 3 2 1 0
1 0 1 1 1 0 1 1

The numeric equivalent is computed by

Local I := Handle(BinaryN.Bit) Do
Sum(2^I * (@I = 1 Or (Not BinaryN)[@I = @I - 1]), I)

which returns 187.

See Also


Comments


You are not allowed to post comments.