Home > Archive > Fortran > October 2004 > overload array index operator
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
overload array index operator
|
|
| Ben Barrowes 2004-10-15, 3:58 pm |
| Is there any way to overload the index operator in fortran? For example
if I want to index an array with a derived type? Or if I want to access
a N-d array with a single subscript (possible of a derived type)?
As a specific example, if I had a 2-d array:
real foo(2,2)
foo(1,1)=10
foo(2,1)=20
foo(1,2)=30
foo(2,2)=40
and if
real bar
bar=[2,4]
then I would like to be able to access:
foo(bar)
and it would be a rank 1 array:
[20,40]
Can this be done in fortran?
| |
| Rich Townsend 2004-10-15, 3:58 pm |
| Ben Barrowes wrote:
> Is there any way to overload the index operator in fortran? For example
> if I want to index an array with a derived type? Or if I want to access
> a N-d array with a single subscript (possible of a derived type)?
>
> As a specific example, if I had a 2-d array:
>
> real foo(2,2)
> foo(1,1)=10
> foo(2,1)=20
> foo(1,2)=30
> foo(2,2)=40
>
> and if
> real bar
> bar=[2,4]
>
> then I would like to be able to access:
> foo(bar)
>
> and it would be a rank 1 array:
> [20,40]
>
> Can this be done in fortran?
No and yes. Unlike C/C++, where anything goes, there is no concept in
Fortran of an index operator. Hence, you can't index an array with a
derived type.
However, in the example you gave, you are trying to index one array
using another array of indices. This is permitted by Fortran 90/95/03;
but these "vector subscripts" work in a slightly different way than your
example above. In fact, for the desired result, your code would have to
read something like this:
---
real foo(2,2)
foo(1,1)=10.
foo(2,1)=20.
foo(1,2)=30.
foo(2,2)=40.
integer bar_1(2), bar_2(2)
bar_1 = (/2,2/)
bar_2 = (/1,2/)
print *,foo(bar_1,bar_2)
---
This would give the desired output "20. 40.". However, a number of
points to note:
1) You need two separate arrays, bar_1 and bar_2, to index foo, since
foo has two dimensions
2) bar_1 and bar_2 *must* be declared as integers if they are to be used
for indexing
3) Array literals are enclosed inside (/.../) delimiters. The [...]
syntax is introduced in the new Fortran 2003 standard; however, it is
*not* part of the F90/F95 standards, and should be avoided if you wish
to write portable code based on these standards.
4) There are restrictions on the use of vector subscripts on the
left-hand side of assignments -- basically, to prevent the same element
being written to twice.
Should you wish to know more, the book "Fortran 95/2003 Explained",
co-authored by c.l.f regular Mike Metcalf, gives a good discussion of
vector subscripts.
cheers,
Rich
--
Dr Richard H D Townsend
Bartol Research Institute
University of Delaware
[ Delete VOID for valid email address ]
| |
| Ben Barrowes 2004-10-15, 3:58 pm |
| Rich,
I appreciate your response. This indexing, however, does not give what
I expected. With your example, I get:
20.0000000000000 20.0000000000000 40.0000000000000 40.0000000000000
In fact, consider:
program zztest1
real foo(3,3)
integer bar_1(3), bar_2(3)
foo(1,1:3)=(/10.,40.,70./)
foo(2,1:3)=(/20.,50.,80./)
foo(3,1:3)=(/30.,60.,90./)
bar_1 = (/2,2,1/)
bar_2 = (/1,3,2/)
print *,foo(bar_1,bar_2)
end program zztest1
Both ifc and g95 yield:
> test1
20.0000000000000 20.0000000000000 10.0000000000000
80.0000000000000 80.0000000000000 70.0000000000000
50.0000000000000 50.0000000000000 40.0000000000000
Where from your post, I expected:
(/20,80,40/)
Ben
Rich Townsend wrote:
> Ben Barrowes wrote:
>
>
>
> No and yes. Unlike C/C++, where anything goes, there is no concept in
> Fortran of an index operator. Hence, you can't index an array with a
> derived type.
>
> However, in the example you gave, you are trying to index one array
> using another array of indices. This is permitted by Fortran 90/95/03;
> but these "vector subscripts" work in a slightly different way than your
> example above. In fact, for the desired result, your code would have to
> read something like this:
>
> ---
> real foo(2,2)
> foo(1,1)=10.
> foo(2,1)=20.
> foo(1,2)=30.
> foo(2,2)=40.
>
> integer bar_1(2), bar_2(2)
> bar_1 = (/2,2/)
> bar_2 = (/1,2/)
>
> print *,foo(bar_1,bar_2)
> ---
>
> This would give the desired output "20. 40.". However, a number of
> points to note:
>
> 1) You need two separate arrays, bar_1 and bar_2, to index foo, since
> foo has two dimensions
> 2) bar_1 and bar_2 *must* be declared as integers if they are to be used
> for indexing
> 3) Array literals are enclosed inside (/.../) delimiters. The [...]
> syntax is introduced in the new Fortran 2003 standard; however, it is
> *not* part of the F90/F95 standards, and should be avoided if you wish
> to write portable code based on these standards.
> 4) There are restrictions on the use of vector subscripts on the
> left-hand side of assignments -- basically, to prevent the same element
> being written to twice.
>
> Should you wish to know more, the book "Fortran 95/2003 Explained",
> co-authored by c.l.f regular Mike Metcalf, gives a good discussion of
> vector subscripts.
>
> cheers,
>
> Rich
>
| |
| Rich Townsend 2004-10-15, 3:58 pm |
| Ben Barrowes wrote:
> Rich,
> I appreciate your response. This indexing, however, does not give
> what I expected. With your example, I get:
> 20.0000000000000 20.0000000000000 40.0000000000000 40.0000000000000
>
> In fact, consider:
>
> program zztest1
>
> real foo(3,3)
> integer bar_1(3), bar_2(3)
> foo(1,1:3)=(/10.,40.,70./)
> foo(2,1:3)=(/20.,50.,80./)
> foo(3,1:3)=(/30.,60.,90./)
> bar_1 = (/2,2,1/)
> bar_2 = (/1,3,2/)
>
> print *,foo(bar_1,bar_2)
>
> end program zztest1
>
>
> Both ifc and g95 yield:
> 20.0000000000000 20.0000000000000 10.0000000000000
> 80.0000000000000 80.0000000000000 70.0000000000000
> 50.0000000000000 50.0000000000000 40.0000000000000
>
> Where from your post, I expected:
> (/20,80,40/)
>
> Ben
Oh dear, I've had a monster brain fart. I apologize for giving you a
wholly-misleading example -- I appear to have forgotten that vector
subscripts along different dimensions are applied independently of one
another.
A better example would have been something like this:
---
real foo(4)
foo(1)=10.
foo(2)=20.
foo(3)=30.
foo(4)=40.
integer bar(2)
bar = (/2,4/)
print *,foo(bar)
---
This will produce the desired output "20. 40."; but note that foo is now
a one-dimensional array, and I have a single index array bar.
cheers,
Rich
--
Dr Richard H D Townsend
Bartol Research Institute
University of Delaware
[ Delete VOID for valid email address ]
| |
| Dick Hendrickson 2004-10-15, 3:58 pm |
|
Ben Barrowes wrote:
> Rich,
> I appreciate your response. This indexing, however, does not give
> what I expected. With your example, I get:
> 20.0000000000000 20.0000000000000 40.0000000000000 40.0000000000000
>
Yep, that's what you get. Vector Valued Subscripts give
the outer product of the subscript ranges. There is no good
way in Fortran to collapse a 2 dimensional array into one
dimension that way you want to.
In your first example, you seem to be thinking of foo(2,2)
as the 4th element of foo. It really isn't, it's the (2,2)
element.
The only thing I can think of is to equivalence a
one dimensional array to foo and subsctipr into
that. Something like
real foo(2,2), big_foo(4)
equivalence (foo, big_foo)
...
the big_foo( (/2,4/) ) will be a rank one array with
values 20 and 40. Unfortunately, you can only EQUIVALENCE
this way with arrays of known size and that aren't dummy
arguments. That's often a killer limitation for most
uses. As Rcih mentioned, you can use a one dimensional
integer scalar array name in place of the array constructor
for the (/ 2,4 /).
You can always copy the 2D array into a 1D array, manipulate
it with vector valued subscripts, and copy it back.
Whether or not that's a good idea depends on how much
you do to what.
Dick Hendrickson
[color=darkred]
> In fact, consider:
>
> program zztest1
>
> real foo(3,3)
> integer bar_1(3), bar_2(3)
> foo(1,1:3)=(/10.,40.,70./)
> foo(2,1:3)=(/20.,50.,80./)
> foo(3,1:3)=(/30.,60.,90./)
> bar_1 = (/2,2,1/)
> bar_2 = (/1,3,2/)
>
> print *,foo(bar_1,bar_2)
>
> end program zztest1
>
>
> Both ifc and g95 yield:
> 20.0000000000000 20.0000000000000 10.0000000000000
> 80.0000000000000 80.0000000000000 70.0000000000000
> 50.0000000000000 50.0000000000000 40.0000000000000
>
> Where from your post, I expected:
> (/20,80,40/)
>
> Ben
>
>
>
> Rich Townsend wrote:
| |
| James Van Buskirk 2004-10-15, 3:58 pm |
| "Ben Barrowes" <barrowes@alum.mit.edu> wrote in message
news:416ff3b7$1_5@news3.es.net...
> Is there any way to overload the index operator in fortran? For example
> if I want to index an array with a derived type? Or if I want to access
> a N-d array with a single subscript (possible of a derived type)?
> As a specific example, if I had a 2-d array:
> real foo(2,2)
> foo(1,1)=10
> foo(2,1)=20
> foo(1,2)=30
> foo(2,2)=40
> and if
> real bar
> bar=[2,4]
> then I would like to be able to access:
> foo(bar)
> and it would be a rank 1 array:
> [20,40]
> Can this be done in fortran?
Sort of. Try this:
module extra_index
implicit none
contains
subroutine repoint(old,n,new)
integer n
real, target :: old(n)
real, pointer :: new(:)
new => old
end subroutine repoint
end module extra_index
program test
use extra_index
implicit none
real, target :: foo(2,2)
integer, allocatable :: bar(:)
real, pointer :: foo1(:)
foo = reshape((/10,20,30,40/),shape(foo))
call repoint(foo,size(foo),foo1)
allocate(bar(2))
bar = (/2,4/)
write(*,*) foo1(bar)
end program test
--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end
| |
| Jason Nielsen 2004-10-15, 3:58 pm |
| On Fri, 15 Oct 2004, James Van Buskirk wrote:
>
>
> "Ben Barrowes" <barrowes@alum.mit.edu> wrote in message
> news:416ff3b7$1_5@news3.es.net...
>
>
>
>
>
>
>
>
> Sort of. Try this:
>
> module extra_index
> implicit none
> contains
> subroutine repoint(old,n,new)
> integer n
> real, target :: old(n)
> real, pointer :: new(:)
>
> new => old
> end subroutine repoint
> end module extra_index
>
> program test
> use extra_index
> implicit none
> real, target :: foo(2,2)
> integer, allocatable :: bar(:)
> real, pointer :: foo1(:)
>
> foo = reshape((/10,20,30,40/),shape(foo))
> call repoint(foo,size(foo),foo1)
> allocate(bar(2))
> bar = (/2,4/)
> write(*,*) foo1(bar)
> end program test
>
> --
> write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
> 6.0134700243160014d-154/),(/'x'/)); end
>
I usually do something along the following:
program zztest1
real foo(3,3)
logical lfoo(3,3)
foo(1,1:3)=(/10.,40.,70./)
foo(2,1:3)=(/20.,50.,80./)
foo(3,1:3)=(/30.,60.,90./)
lfoo=.false.
lfoo(1,2)=.true.
lfoo(2,(/1,3/))=.true.
print *, pack(foo,mask=lfoo)
end program zztest1
of course you won't get the order that you had specified (column-wise as
opposed to row-wise). Now all you have to do is update lfoo as you
iterate through your program to pull off the elements you want. Not sure
if this is what you were going for though.
Cheers,
Jason
|
|
|
|
|