The exercises involving arrays up to this point may have made the point that further smoothing is needed in how Sisal handles arrays. There are indeed facilities to make array operations friendlier. For instance, we can acquire the size and bounds of arrays by using the Sisal primitives array_size(), array_liml(), and array_limh(), respectively. These primitives allow us to form loops such as the following:

```for i in array_liml(X), array_limh(X) cross
j in array_liml(X[i]), array_limh(X[i])
q := deep_thought(x, i, j)
returns array of q
end for```
This sort of thing can be very useful when dealing with ragged arrays. For instance, the situation often develops in the modeling of physical systems that a system array with unchanging boundaries must be evolved and returned with the original boundaries. In the following example, we will use the Sisal operator for array concatenation, "||".

```let new_core   := evolve(system_array);        % new system with no boundaries
top_indx   := array_liml(system_array);    % positions of top and bottom
btm_indx   := array_limh(system_array);    %   boundaries from old system

new_top    := system_array[top_indx];      % actual top boundary
new_btm    := system_array[btm_indx];      % actual bottom boundary

left_indx  := array_liml(system_array[1]); % positions of left and right
right_indx := array_limh(system_array[1]); %   boundaries from old system
%   (all rows have same indices)

long_core  := for row in core at i         % add side boundaries to all rows

left_bound_elt    := system_array[ i, left_indx ];
left_bound_array  := array [ 1: left_bound_elt ];
right_bound_elt   := system_array[ i, right_indx ];
right_bound_array := array [ 1: right_bound_elt ]

returns array of left_bound_array || row  || right_bound_array
end for;

long_tall  := new_top || long_core || new_btm   % add top and bottom

in  long_tall   % The newly evolved system, complete with boundaries.
end let```
This elegant bit of code builds a new system array in three steps. First, function "evolve" is invoked with the old system array as its argument, and the new system array is returned. Then the short rows of the new array are made the correct length by concatenating the boundaries from the rows of the old system array onto them in a for-loop. In the loop we see that the values left_bound_elt and right_bound_elt are made into arrays of one element. This is necessary, as the concatenation operator requires that both its arguments be arrays. Finally, the top and bottom rows of the old system array are concatenated onto the widened core of the new system array. The result of these steps is an array the same size and shape as the old system array.

This example also includes a use of the hybrid range generator "row in core at i". Use of this form of generator relieves us of the need to specify the index range of the array core, and yet still gives us an index value to use in the array index expressions within the loop.

These techniques are so powerful and useful that we will explore them further in an exercise.