Array-valued functions: Very basic questions

  • Follow


Hi!

I have a question since my early Fortran times (and I apologize if this
question has been asked frequently before and just missed by my Google
search). According to several references, Fortran90 or later supports
array-valued functions. However, I cannot find any *basic* example in
the web or textbooks of the construction and calling of the simplest
case of such a function. There are lots of discussions of array-valued
functions, but they are all dealing with more complex constructions like
allocatable arrays or interfaces etc.

Let there be a function that has a real argument t and returns an array
of dimension three. For example, f could contain the three-dimensional
trajectory of a body as a function of time:

function trajectory(t)
   real :: t,f1,f2,f3
   real, dimension(3) :: trajectory
   trajectory(1) = f1(t)
   trajectory(2) = f2(t)
   trajectory(3) = f3(t)
   return
end function trajectory

program testarray
   real :: x
   real, dimension(3) :: a,trajectory
   x = 1.75!some arbitrary value
   a=trajectory(x)
   write(*,*) a
end program testarray

This example leads to warnings like

Warning: Extension: REAL array index at (1)

and the returned values are all zero. So, obviously array-valued
functions are working differently, and are not what one would naively
expect, namely array-analogs of the well-known functions with a single
value. From the highly specific search hits on the web I guess that the
dimension of the function is connected somehow with the arguments list
(rather than being independent of it as in single-valued functions). But
what exactly are array-valued functions, and how are they used?

This question is, at least to me, rather an academical one since I would
never try to use such a construction but would rather use a subroutine
which can return variables and array of any type and dimension you want.
I simply would like to know what array-valued functions really are :-)

Ingo

0
Reply ingo.thies (51) 1/18/2012 4:29:33 PM

"Ingo Thies" <ingo.thies@gmx.de> wrote in message 
news:9noabdF95vU1@mid.individual.net...

Change as noted below to provide an explicit interface to
function trajectory in program testarray and recompile:

module stuff
> function trajectory(t)
>   real :: t,f1,f2,f3
>   real, dimension(3) :: trajectory
>   trajectory(1) = f1(t)
>   trajectory(2) = f2(t)
>   trajectory(3) = f3(t)
>   return
> end function trajectory

end module stuff

> program testarray
use stuff
>   real :: x
>   real, dimension(3) :: a,trajectory
>   x = 1.75!some arbitrary value
>   a=trajectory(x)
>   write(*,*) a
> end program testarray

-- 
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end


0
Reply not_valid (1681) 1/18/2012 4:42:20 PM


On 18.01.2012 17:42, James Van Buskirk wrote:

> Change as noted below to provide an explicit interface to
> function trajectory in program testarray and recompile:

So, in other words, array-valued functions are not to be used
stand-alone but are kind of "merried" with the module environment, as
far as I can see here.

Other question: If one wants to assign the result of trajectory to
separate single-valued real values, e.g. x,y,z, what would be the
correct syntax? Neither

x=trajectory(t)(1)

or

x=trajectory(t,1)

works. So I suppose that, again, a more sophisticated calling is required.

The final question is then, of course, what are the actual benefits of
using array-valued functions? Apparently, they cannot be used as
straightforward array-versions of classical functions, while the same
task can be done with a subroutine and much less pain...

subroutine get_trajectory(t,trajectory)
   real :: t
   real,dimension(3) :: trajectory
   ...

Ingo

0
Reply ingo.thies (51) 1/18/2012 5:04:38 PM

Ingo Thies <ingo.thies@gmx.de> wrote:

> I have a question since my early Fortran times (and I apologize if this
> question has been asked frequently before and just missed by my Google
> search). According to several references, Fortran90 or later supports
> array-valued functions. However, I cannot find any *basic* example in
> the web or textbooks of the construction and calling of the simplest
> case of such a function. There are lots of discussions of array-valued
> functions, but they are all dealing with more complex constructions like
> allocatable arrays or interfaces etc.
....
> function trajectory(t)
>    real :: t,f1,f2,f3
>    real, dimension(3) :: trajectory
>    trajectory(1) = f1(t)
>    trajectory(2) = f2(t)
>    trajectory(3) = f3(t)
>    return
> end function trajectory
> 
> program testarray
>    real :: x
>    real, dimension(3) :: a,trajectory
>    x = 1.75!some arbitrary value
>    a=trajectory(x)
>    write(*,*) a
> end program testarray

That stuff about interfaces that you wanted to ignore is exactly the
problem.

Array-valued functions *REQUIRE* an explicit interface. For that matter,
so do pretty much all of the features that were new to f90 or later and
relate to invoking procedures.

You don't necessarily have to write an interface body to get an explicit
interface. That's one fairly common misunderstanding. That is one way to
get an explicit interface, but it is not the only way, and it is usually
the worst.

Note that your declaration in the main program

 real, dimension(3) :: a,trajectory

is a perfectly valid form for a declaration of a local array trajectory
(along with the local array "a"). There is no way for the compiler to
know that this is supposed to be a function. (The compiler doesn't get
to look outside of the main program to figure this out). That's why it
is complaining about the nonstandard real index. It thinks that
trajectory is just a local array and that trajectory(a) is just a
reference to elements of that array (having a real index is an extension
in some compilers).

The syntax for function references and array element references is
simillar enough that the compiler can't just deduce which it is; you
have to tell it. For a scalar function, you don't have the same degree
simillarity. If the compiler sees a syntax like f(x) and f hasn't been
declared as an array, then it must be a function. But that way of
distinguishing doesn't work for array-valued functions.

Explicitly declaring the EXTERNAL attribute would at least make sense. I
personally recommend that even for f77 codes. The f77 standard also
recommends the EXTERNAL declaration because of the possibility of
conflicts with vendor intrinsics. That particular recommendation is
often ignored. Usually no problems result; on occasion, they do.

But the EXTERNAL attribute alone could not adequately handle all the
kinds of things introduced in f90. You need an explicit interface
instead. You *COULD* (but I would not recommend it; see below) replace
the above statement with

  real, dimension(3) :: a   !-- This one is separate now
  interface
     function trajectory(t)
       real :: t
       real, dimension(3) :: trajectory
     end function trajectory
  end interface

I just did this and it works fine (after I also replaced the references
to f1, f2, and f3 by trivial expressions so as to avoid bothering to
come up with those 3 functions).

I recommend making putting most procedures in modules for new code.
USEing a module automaticaly provides an explicit interface for the
procedures in the module. Internal procedures also have explicit
interfaces automatically, but they work best for smaller things.

If you use either an internal or module procedures, be sure to remove
the declaration of trajectory from the main program. The needed
information comes in via the automatically generated interface - not by
f77-style duplicate declaration in the invoking scope.

If you want to do much of anything with f90, modules are among the first
things you need to look at. Many of the other features of the language
work much more cleanly with modules.

-- 
Richard Maine                    | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle           |  -- Mark Twain
0
Reply nospam47 (9742) 1/18/2012 5:10:49 PM

Ingo Thies <ingo.thies@gmx.de> wrote:

> On 18.01.2012 17:42, James Van Buskirk wrote:
> 
> > Change as noted below to provide an explicit interface to
> > function trajectory in program testarray and recompile:

(and also delete the declaration of trajectory from the main program or
it won't work).

> So, in other words, array-valued functions are not to be used
> stand-alone but are kind of "merried" with the module environment, as
> far as I can see here.

No, that's not required. But I do recommend it. More to the point, I
recommend that *EVERYTHING* be put in modules in new f90 code.

> Other question: If one wants to assign the result of trajectory to
> separate single-valued real values, e.g. x,y,z, what would be the
> correct syntax?

There is no such functionality. You'd have to do something like return
the result into an array and then copy the array elements to the
separate scalar variables.

> The final question is then, of course, what are the actual benefits of
> using array-valued functions?

Lots of them, but I'll not try to get into that. If the application has
a function that is naturally array-valued, then it is handy to write it
that way. There really is no more esoteric reason.

> Apparently, they cannot be used as
> straightforward array-versions of classical functions, while the same
> task can be done with a subroutine and much less pain...

I wouldn't want to talk you out of using subroutines. Yes, they are
often a better a choice. But not for any reason so trivial as that this
particular case can be done without explicit interfaces. Many things for
subroutines also require explicit interfaces. As I said before, modules
are pretty important in f90. If you go out of your way to avoid using
modules, you are going to find the whole language pretty awkward. I've
seen people do that.

-- 
Richard Maine                    | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle           |  -- Mark Twain
0
Reply nospam47 (9742) 1/18/2012 5:21:04 PM

"Ingo Thies" <ingo.thies@gmx.de> wrote in message 
news:9nocd6Fp1gU1@mid.individual.net...

> So, in other words, array-valued functions are not to be used
> stand-alone but are kind of "merried" with the module environment, as
> far as I can see here.

You can get by without modules in this case; modules are just the
easiest way to provide an explicit interface.  You could write out
an interface block for function trajectory:

interface
   function trajectory(t)
      real t
      real trajectory(3)
   end function trajectory
end interface

and place this among the declarations of its calling program unit.

> Other question: If one wants to assign the result of trajectory to
> separate single-valued real values, e.g. x,y,z, what would be the
> correct syntax? Neither

> x=trajectory(t)(1)

> or

> x=trajectory(t,1)

> works. So I suppose that, again, a more sophisticated calling is required.

x = dot_product([1,0,0],trajectory(t))

or

x = sum(trajectory(t),mask=[(i==1,i=1,3)])

> The final question is then, of course, what are the actual benefits of
> using array-valued functions? Apparently, they cannot be used as
> straightforward array-versions of classical functions, while the same
> task can be done with a subroutine and much less pain...

> subroutine get_trajectory(t,trajectory)
>   real :: t
>   real,dimension(3) :: trajectory
>   ...

The benefit in functions lies in their use in expressions.  If you
don't intend to use them in expressions, subroutines probably are
more natural.  There are cases, e.g. specification expressions,
where you can't directly call a subroutine and would have to write
a function that invoked your subroutine to make it available in
such a context (i.e. the specification part of a program unit.)
If you found yourself having to do that, you likely would simply
rewrite your subroutine as a function.

An example that comes to mind is a Runge-Kutta integrator.  If
you had a subroutine that worked OK for 1 dependent variable, it
would probably have a dummy argument function that returned a
scalar value.  To make this integrator work for n dependent
variables, you could change it around a bit, one of the changes
being to make the dummy argument function return an array.

-- 
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end


0
Reply not_valid (1681) 1/18/2012 5:30:53 PM

Hi Richard,

On 18.01.2012 18:10, Richard Maine wrote:

> is a perfectly valid form for a declaration of a local array trajectory
> (along with the local array "a"). There is no way for the compiler to
> know that this is supposed to be a function. (The compiler doesn't get
> to look outside of the main program to figure this out). That's why it

Ah, many thanks, this explains it!

And also thanks for the other replies. As I said, there is no current
problem where I would need such functions, but more a question of basic
understanding.

I am quite familiar with modules and making extensively use of them.
Nevertheless, there are some questions left concerning modules, to which
I may return later, when I have a particular example.


Best wishes,

Ingo

0
Reply ingo.thies (51) 1/18/2012 5:45:01 PM

James Van Buskirk <not_valid@comcast.net> wrote:

> The benefit in functions lies in their use in expressions.  If you
> don't intend to use them in expressions, subroutines probably are
> more natural.

Ah yes. I've made that point before, but I was distracted by other
questions and didn't do so this time, except by an indirect allusion. I
do regard it as a pretty big point.

If you have a function whose only use is in the trivial form

  y = f(x)

then there really isn't much advantage to it being a function, and there
can be lots of disadvantages. Many of the complicated and controversial
aspects of functions (in particular, all the stuff about side effects)
come about because function references can be used in the middle of more
complicated expressions. If you are never going to use that capability,
then you might well be better off not paying the costs of it in terms of
the "odd" restrictions on functions.

-- 
Richard Maine                    | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle           |  -- Mark Twain
0
Reply nospam47 (9742) 1/18/2012 6:13:16 PM

Richard Maine <nospam@see.signature> schrieb:

> No, that's not required. But I do recommend it. More to the point, I
> recommend that *EVERYTHING* be put in modules in new f90 code.

I think putting the main program or another module inside a module
is frowned upon ;-)
0
Reply tkoenig1 (168) 1/18/2012 7:57:02 PM

8 Replies
65 Views

(page loaded in 0.093 seconds)

Similiar Articles:













7/18/2012 9:47:53 AM


Reply: