Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Using loop variable in variable name 2

Status
Not open for further replies.

Philipp12345

Programmer
Dec 8, 2011
3
DE
Hi,

i have the following problem. I'm given several variables

REAL v1, v2, v3 ... v100. These variables can not bei given in an array!

Now i try to write a loop, that gives each variable a value. In this loop i try to use the value of the loop variable i to reference to to variable i like to give a value. Somthing like

do 20 i = 1, 100, 1
v=i
enddo

is this possible?


 
Hhhmmm...that looks a bit like what SPSS allows, doesn't it? In fortran, this is not going to fly.

I have never had the need for certain memory "tricks", but there may be a way to what you want.

I have not done it myself, so, hopefully somebody else will come up and tell the correct way of doing it...but...

I think you may have to start by declaring your variables and putting them in a common block...that way, you know they are adjacent...then, you can probably declare some kind of array variable or pointer and make it point to v1...and THEN, I suspect you may be able to loop through it.

I hope this takes you one step closer to what you want.

Curious, though, why do you say that v1, v2, v3, ...cannot be in an array?
 
The Variables are given to me by PAW (it's a tool for particle physics) so i can not change them or use a simple array...
 
Is PAW a pre-compiled library to which you pass individual variables via some subroutine parameters? or what? 'cause this wouldn't stop you from using an array...

can you post a sample use of your v1, v2, ..., vn variables? It still isn't clear to me why you personally cannot use an array...
 
PAW is a pre-compiled library. It gives me a set of 200 variable, sth. like

x1,y1,z1,vx1,...,x2,y2...

i have no influence on this variables. of course i can write them in an array. but that's my point: can i create a loop that allows me to do so?
 
I have no idea what PAW is, so bear with me, here.

In any case, please, stop just describing the problem and provide ACTUAL code or files of how PAW "provides" you these 200 variables or how you use it.
 
Do you mean EQUIVALENCE, i.e. something like this?
Code:
program equiv
  integer :: v1, v2, v3, i
  integer, dimension(3) :: v_arr
  equivalence (v1, v_arr(1)), (v2, v_arr(2)), (v3, v_arr(3))
  
  v1 = 0
  v2 = 0
  v3 = 0
  v_arr = 0
 
  write(*,*) v1, v2, v3
  write(*,*) (v_arr(i), i=1,3 )
  
  do i=1, 3
    v_arr(i) = i*11
  end do  

  write(*,*) v1, v2, v3
  write(*,*) (v_arr(i), i=1,3 )
  
end program equiv
Output
Code:
$ g95 equiv.f95 -o equiv

$ equiv
 0 0 0
 0 0 0
 11 22 33
 11 22 33
 
I guess that works.

In order to reduce the amount of work in the equivalence statement (explicitly listing equivalence for every item in the array), one can place the variables vn in a common block and then just equivalence the first variable...like this:

Code:
program equiv
  integer :: i
  integer, dimension(1) :: v_arr
  integer ::   v1, v2, v3
  common       v1, v2, v3
  equivalence (v1, v_arr)

and while the dimension of the array does not need to be more than 1, I guess it would be best to make it compatible to guard against mistakes. etc.
 
salgerman said:
...and while the dimension of the array does not need to be more than 1...
Why?
I tried what you said and it works.
But I don't understand why the array dimension need to be only 1 when i have 3 variables...

 
I did not say that the dimension needs to be only 1...I said, it does not need to be more than 1.

In any case, what exactly is the part that you do not understand? Allow me mention a couple of things that might help make things clear.

In my version of declaring variables:

The very first trick consists in declaring all separate 200 variable v1, v2, v3,...,v200 in a common block...this is key for it to work because a common block guarantees that such variable's memory addresses will be adjacent in memory, very much like what happens when you declare an array.

Then, the array does not need to be dimension all the way to 200 because we do not really need the array memory. We are using the array variable as a pointer, if you will; the memory to be pointed to has already been "allocated" when v1, v2, ..., v200 were declared.

Hope this helps.

The thing
 
Hi salgerman,
I tried this
Code:
[COLOR=#a020f0]program[/color] equiv
  [COLOR=#2e8b57][b]integer[/b][/color] :: v1, v2, v3, i
  [COLOR=#2e8b57][b]integer[/b][/color], [COLOR=#2e8b57][b]dimension[/b][/color]([COLOR=#ff00ff]1[/color]) :: v_arr
  [COLOR=#2e8b57][b]common[/b][/color] v1, v2, v3
  [COLOR=#2e8b57][b]equivalence[/b][/color] (v1, v_arr)
  
  v1 [COLOR=#804040][b]=[/b][/color] [COLOR=#ff00ff]0[/color]
  v2 [COLOR=#804040][b]=[/b][/color] [COLOR=#ff00ff]0[/color]
  v3 [COLOR=#804040][b]=[/b][/color] [COLOR=#ff00ff]0[/color]
  v_arr [COLOR=#804040][b]=[/b][/color] [COLOR=#ff00ff]0[/color]
 
  [COLOR=#804040][b]write[/b][/color]([COLOR=#804040][b]*[/b][/color],[COLOR=#804040][b]*[/b][/color]) v1, v2, v3
  [COLOR=#804040][b]write[/b][/color]([COLOR=#804040][b]*[/b][/color],[COLOR=#804040][b]*[/b][/color]) (v_arr(i), i[COLOR=#804040][b]=[/b][/color][COLOR=#ff00ff]1[/color],[COLOR=#ff00ff]3[/color] )
  
  [COLOR=#804040][b]do[/b][/color] i[COLOR=#804040][b]=[/b][/color][COLOR=#ff00ff]1[/color], [COLOR=#ff00ff]3[/color]
    v_arr(i) [COLOR=#804040][b]=[/b][/color] i[COLOR=#804040][b]*[/b][/color][COLOR=#ff00ff]11[/color]
  [COLOR=#804040][b]end do[/b][/color]  

  [COLOR=#804040][b]write[/b][/color]([COLOR=#804040][b]*[/b][/color],[COLOR=#804040][b]*[/b][/color]) v1, v2, v3
  [COLOR=#804040][b]write[/b][/color]([COLOR=#804040][b]*[/b][/color],[COLOR=#804040][b]*[/b][/color]) (v_arr(i), i[COLOR=#804040][b]=[/b][/color][COLOR=#ff00ff]1[/color],[COLOR=#ff00ff]3[/color] )
  
[COLOR=#a020f0]end program[/color] equiv
an it works:
Code:
$ g95 equiv2.f95 -o equiv2

$ equiv2
 0 0 0
 0 0 0
 11 22 33
 11 22 33
But I'm curious if it's legal when I declared the array only with dimension = 1 and in the loop I try to access elements with index greater than 1 ?
 
Well, I don't know about legal matters, I am not a "lawyer"...

I usually consult with more than one "lawyer" (compiler) before deciding whether something is going to work or not...sometimes even on different platforms (Linux, Windows).

Also, it all depends on which flags you use to compile a program. I usually also include run-time array-bounds checking...and I think that if we include that in the program above, it may actually complain at run time...

...and for other things, it may actually be prudent to simply declare the array with as many items as needed. Remember, unlike in C where an array is just another variable, in Fortran an array is more like an object with attributes.

In any case, previously, I had inherited many Fortran program where they definitely declared arrays of just length 1 when they were the argument in a subroutine...for as long as the array that you were passing had already been declared to 100, the array instance 'local' to the subroutine could be also addressed all the way to 100.

Like I said, though, it is probably safest to declared things consistently for either compilation checks or runtime checks.


 
It is legal syntactically because you are accessing an array. So you could do this
[CODE syntax]
integer x(10)
...
x(11) = 1
[/code]
That would compile but it may or may not work. Fortran stores its data as it is declared. So if you declared
Code:
integer x(10), oops1, oops2
oops1 = 20
oops2 = 21
x(11) = 31
x(12) = 32
print *, oops1, oops2
You will get 31 and 32. By making the dimension size 1, you are using this feature.

Another problem is that this is largely dependent upon the word alignment and storage type.
Code:
integer, target::x(3)
integer, target::x1, x2, x3
equivalence (x1, x(1))
x1 = 1111
x2 = 2222
x3 = 3333
print *, x(1), x(2), x(3)
You may or may not get 1111, 2222, 3333.

On some debuggers, you may sometimes get the warning that you've exceeded the array bounds if the array is declared as (1) and the program is accessing an index greater than 1.

Another potential issue with huge lists of variables is missing commas. Since spaces are legal in variable names in Fortran, (you can get round this problem with implicit none)
Code:
integer x1, x2, x3 x4, x5
integer (x1,x(1))
x(3) = 1
x3 = 2
print *, x(3)
You will get 1.
 
Hi salgerman,
OK, it's clever, you deserve a star :)
I had inherited many Fortran program where they definitely declared arrays of just length 1....
Hey man, I'm in similar situation: I'm inherited tons of legacy COBOL programs...
 
Say, would anyone know how to implement a similar solution using pointers? I am yet to use pointers in Fortran and would be interested in seeing an example with it instead of the equivalence statement.
 
Alas,
Code:
integer x(10)
x(11) = 1
is illegal code in Fortran. Fortran Standard, 6.2.2.1:
The value of a subscript in an array element shall be within the bounds for that dimension.

It's wrong statetement:
Fortran stores its data as it is declared.
[/code]
It's true for common block lists only, othewise no storage associations for ordinal declaration lists.

Please, don't muddle OP author (and others) by some FTN compilers peculiarities...
 
Hmmm,
I use pointers quite heavily, but I cannot think of moving pointers from one variable to the next as Fortran dereferences them when you reference them. You cannot get the adress in memory that way and then modify it. At least as far as I know. And we should come up with something easier than just
v1 = 1
v2 = 2
v3 = 3...

But just as a piece of brainjogging...

Code:
type myvariables              ! create a slab of memory for the variables
real v1, v2, v3 ...... v200
end type

integer iPos1, iPos, j
real rVal

type (myvariables) tMV

iPos1 = loc (tMV.v1)           ! get location in memory for the first item

do j = 1, 200
  iPos = iPos1 + (j-1) * 4     ! advance 4 byte for every value
  rVal = float (j)             ! define the value you want to assign to the variable v[j]
  call CopyMemory (iPos, rVal, 4)! copy it to the proper place in memory
enddo

Just an idea. I do not know if CopyMemory is available in all FORTRAN implementations or if there is an equivalent.

But this has some disadvantages:
You would have to reference the data as tMV.v1, tMV.v2 etc. when passing them to the subroutine.

But I would be afraid of such coding, to write data to memory that way. In case of an error (e.g. number of elements in myvariables is smaller than the number of cycles in the loop) you may achieve things that might be more than just a little bit unpleasant.

Norbert
 
After a long night musing about this problem I came up with some additional ideas to put some sort of safety into my code.

Code:
type myvariables              ! create a slab of memory for the variables
[b]SEQUENCE                      ! make sure the sequence of the data in memory is as in the type declaration[/b]
real v1, v2, v3 ...... v200
end type

integer iPos1, [b]iPos2, iSize,[/b] iPos, j
real rVal

type (myvariables) tMV

iPos1 = loc (tMV.v1)           ! get location in memory of the first item
[b]iPos2 = loc (tMV.v200)         ! get location of last item
iSize = sizeof (rVal)          ! get bytecount of variable[/b]

do j = 1, 200
  iPos = iPos1 + (j-1) * iSize ! advance 4 byte for every value
[b]  if (iPos .gt. iPos2) cycle   ! check if position is in legal range[/b]
  rVal = float (j)             ! define the value you want to assign to the variable v[j]
  call CopyMemory (iPos, rVal, iSize)! copy it to the proper place in memory
enddo

This puts some safety into the coding as to not access memory outside of the data. But if this is better than

Code:
real v1, v2, v3, .... , v200
v1 = ....
v2 = ....
v3 = ....
.
.
.
v200 = ...

might be depending on how many different sets of values occur during the execution of the program.

Norbert
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top