Fortran 90 functions can return array-valued results. A number of intrinsic functions always return array values and most intrinsic functions can return array values. In addition, user-written functions may be array valued. Array-valued functions provide two (related) tremendous benefits. First, array-valued functions may be used as operands in array-valued expressions, allowing data-parallel computations to be expressed in the most natural forms. Second, this facilitates composing a computation from array-valued subexpressions, which often can be evaluated in parallel. Thus array-valued functions provide increased opportunities to combine process parallelism with data parallelism in a natural way.
For example, where and are large conformable two-dimensional arrays, + cshift(u,1,2) - cshift(u,-1,2) is an expression similar to some commonly found in seismic modeling computations. (CSHIFT is the intrinsic function that circularly shifts the first argument - an array - the amount of the second argument along the dimension specified by the third argument.) This expression very clearly expresses the nature of the computation, which the following diagram stylizes for a single element of the result of the expression,
once one is thinking data parallel, and offers the compiler the opportunity to evaluate any subexpressions in parallel. This might be particularly appropriate, for example, if g were itself a function reference, in which case it probably would be advantageous to evaluate the subexpression cshift(u,1,2)-cshift(u,-1,2) in parallel with the evaluation of .
The two categories of array-valued intrinsic functions are known as ``transformational" and ``elemental" functions. A transformational function accepts an input array and produces a different array as the result-it ``transforms" the input array into something else, possibly even a differently shaped array, or even a scalar. (A transformational function can even transform a scalar into an array.) CSHIFT is an example of a transformational function, albeit a very simple one with a result that is conformable with its (first) argument. Intrinsic function MATMUL (matrix multiplication) is an example of a transformational function that returns an array result of different shape than (either of) its arguments. The reduction functions, SUM, PRODUCT, COUNT, etc., are examples of transformational functions that ``reduce" array arguments to scalar results. The Fortran 90 array transformational intrinsic functions (42 in all) are listed in Table 4.
The elemental intrinsic functions are (most of) those defined with scalar dummy arguments. Such functions may be called with array actual arguments, and return an array result conformable with the actual argument. Each element of the result is what would have been obtained if the function had been called with just the corresponding (scalar) element of the actual argument. Thus an elemental function is automatically (and conceptually in parallel) applied to each element of the actual argument. Any of the usual computational intrinsic functions can be called elementally. For example, in
COS(X)X may be scalar, in which case COS returns a scalar result, or X may be an array (any dimension), in which case COS returns an array-valued result conformable with X. If the seismic-like example above were modified to
exp(g) + cshift(u,1,2) - cshift(u,-1,2)then each term in the expression becomes an intrinsic function call that returns a result conformable with g and u. The first term is an elemental call and the other two are transformational. All of the 108 Fortran 90 intrinsic functions may be called elementally except for the 42 listed above as transformational.
Note that whereas elemental function calls may be considered to be a number of independent scalar function calls, a transformational function is considered as in integral self-contained computation, delivering the result ``all together, all at once".
Fortran 90 provides 21 intrinsic array functions, some (such as SIZE) that allow inquiries to be made about array properties and others that either construct arrays or extract information from arrays. These functions, all of which are transformational, are listed in Table 5
Users may define array-valued functions; all such functions are transformational. Function F18 in the previous section is an example of a user-defined array-valued function. In this case the shape of the array is determined (dynamically) from arguments, such as the shape properties of an array argument; this is probably the most useful form of array- valued functions. See also the examples in sections 4.5.
Note that function results are declared to be array- valued with ordinary declaration statements, as if the function name is an ordinary variable (as indeed it is within the body of the function). Though automatic arrays may be the most useful form for user-defined array-valued functions, any other form is also valid: explicit-shape array, allocatable array, pointer array. These are also declared and used in the procedure as if the function name were just another variable. The main additional requirement is that the array value must be fully defined before returning from an execution of the function. On the other end of things, the interfaces of array-valued functions must be explicit where such functions are used, so that the caller knows that it's dealing with a function that is array-valued.
A simple example of an array-valued function definition will complete this section. Suppose that the partial sums of a one-dimensional array of n elements are needed in an array expression-that is, the kth value needed is sum(P(1:k)). An array-valued function is ideal for delivering the requisite set of values (although in this simple case it might be almost as good to use the array constructor in the expression rather than the call to Partial_sums):
function Partial_sums(P) real P(:) ! Assumed-shape dummy array real Partial_sums(size(P)) ! The partial sums to be returned integer k Partial_sums = (/(sum(P(1:k),k=1,size(P))/) ! This is functionally equivalent to ! do k=1,size(P) ! Partial_sums(k) = sum(P(1:k)) ! end do ! but the do loop specifies a set of sequential ! computations rather than parallel computations end function Partial_sums
The following more complicated examples of data parallel computations are also configured to deliver results as user-defined array-valued functions.