for i in 1, n for a in a_array for a in a_array at iHere, we see the two familiar forms followed by a new hybrid form. The first one, as we have said, generates a set of independent values, all named

In addition to the above simple range generators, we can have compound or multidimensional ones, such as the following:

for i in m, n cross j in p, q for a in a_array cross b in b_array for k in r, s dot m in x, y for c in c_array dot d in d_arrayHere we have the first two forms of simple range generator combined into compound generators. The keywords "dot" and "cross" determine how the index or element value sets will be formed. In the first example, we have an index value set consisting of the cross product of the ranges from the two simple generators. In other words, every value of

In the dot-product range generators, we again have a pairing of values, but no value from either set is used more than once. In the third example, the first value of `k` will be r, and it will be paired with the first value of `l`, which will be x. The second values of `k` and `l` will be r+1 and x+1, respectively; this continues until the smaller-sized range is completely used up, at which point the loop's range is complete. Similarly, in the fourth example, the first value of `c` will be the first element of `c_array`, and it will be paired with the first element of `d_array`. In the case of arrays, when we use the term "first element" we mean the element with the lowest index.

These compound ranges are very powerful, and they can be extended effectively without limit. Semantically, compound ranges are equivalent to loop nests. The following two loops are equivalent in range and in the types of values they return:

for i in m, n cross j in p, q x := i + j returns array of x value of sum x end for for i in m, n x_array, x_scalar := for j in p, q x := i + j returns array of x value of sum x end for returns array of x_array value of sum x_scalar end forEach of the above two loops produces a two-dimensional array and a scalar. The index range of the arrays are [m, n] by [p, q], and the elements in corresponding positions of the arrays are identical.

Just to make certain all this is clear, we should consider a few more examples. The following loop sums all the elements of a two_dimensional array:

for i in 1, num_rows cross j in 1, num_columns returns value of sum a[i,j] end forThe next example returns the vector consisting of the major diagonal of a two_dimensional array:

for i in 1, num_rows dot j in 1, num_columns returns array of a[i,j] end forThe above example is a bit tricky. Since the array

Here's another tricky example, the transposition of a rectangular array:

for j in 1, num_columns cross i in 1, num_rows returns array of a[i,j] end forThe above deserves careful study. It hasn't been emphasized yet, but the shape of an array returned by a loop nest is strictly determined by the order of the ranges of the loops as well as the ranges themselves. The array

for B_val in Boolean_array returns value of sum B_val value of product B_val end forThis loop's return clause performs the inclusive "or" and the "and" functions of the elements of

The catenate reduction produces a flattened aggregate. This latter point is nontrivial. If a two-level loop produces an array via the "array of" aggregation, it will be a two-dimensional array, with ranges corresponding to those of the loops. If, on the other hand, it returns "value of catenate", the resulting array will be one-dimensional, consisting of all the rows produced by the inner loop joined together into a larger vector. A few examples at this point would likely be helpful.

The following loop finds the largest and smallest elements of a three-dimensional array:

for a_two_d in a_three_d cross a_one_d in a_two_d cross a_elt in 1, a_one_d returns value of least a_elt value of greatest a_elt end forThe following loop flattens a three-dimensional array into a one_dimensional vector by concatenating all its rows together:

for row_plane in a_3d_array cross row in row_plane returns value of catenate row end forThe number of elements of the array resulting from the above loop will be the sum of the numbers of elements in each row of

for val in an_array returns value of sum 1 when val > 0.0 end forThe above loop sums a set of integer ones corresponding to the positive elements of

for i in 1, number_of_values new_val := deep_thought( value[i] ) returns array of new_val unless new_val > upper_val | new_val < lower_val end forThe above loop returns an array of only those instances of

The for-loop is incapable of doing the sorts of calculations that involve information moving between the instances of the loop's calculation, such as iterative convergence calculations. It is also incapable, as we now know it, of doing the more general form of looping across irregular index ranges or value sets. The first of these problems will be dealt with when we discuss the other form of Sisal loop statement, and the second when we deal with advanced looping concepts. For now, let us do a few exercises that will expose more of the power of Sisal loops.