This question already has an answer here:
Refering to an associating-name in another associating entity in the same ASSOCIATE construct
(1 answer)
Closed 1 year ago.
The ASSOCIATE feature is in principle quite useful, as it allows assigning values to names without incurring the syntactical overhead of explicitly defining local variables. However, its utility is limited because the values are bound only after the ASSOCIATE statement. This quickly leads to the need for nested associate statements, e.g.
PROGRAM toyexample
IMPLICIT NONE
ASSOCIATE(a => 2)
ASSOCIATE(b => [a, 2*a, 3*a])
PRINT *, a
PRINT *, b
END ASSOCIATE
END ASSOCIATE
END PROGRAM toyexample
In Lisp terms, this would be the behavior of let, while I seek to emulate the behavior of let*, which would allow me to write
PROGRAM toyexample
IMPLICIT NONE
ASSOCIATE(a => 2, b => [a, 2*a, 3*a])
PRINT *, a
PRINT *, b
END ASSOCIATE
END PROGRAM toyexample
Is there any feature in Fortran that allows me to do this?
program toyexample1
implicit none
integer, parameter :: a = 11
integer, parameter :: b = 22
integer, parameter :: c = 33
associate(a => b, b => c, c => a)
print *, a, b, c ! Intel Fortran print: 22 33 22, other 22 33 11
end associate
block; integer, parameter :: a = b, b = c, c = a ! NAG Fortran rejected
print *, a, b, c ! All compilers print: 22 33 22
end block
block; integer, parameter :: a = 2, b(*) = ([a, 2*a, 3*a]) ! PGfortran crash
print *, a, b(:) ! All compilers print 2 2 4 6
end block
end program toyexample1
Related
I have the following simple Fortran code
program test
type vec
integer :: x(3)
end type
type(vec) :: v(2)
call sub(v)
contains
subroutine sub (v)
class(vec), intent(in) :: v(:)
integer :: k, q(3)
q = [ (v(1)%x(k), k = 1, 3) ] ! <-- fails here
end subroutine
end program
which, when compiled by GNU Fortran 11 (but not other versions) with -fcheck=bounds, fails with the error
At line 19 of file test.f90
Fortran runtime error: Index '3' of dimension 1 of array 'v%_data%x' above upper bound of 2
It look as if the compiler simply interchanged the lengths of x and v and this can be confirmed by changing the numbers.
When the word class is replaced by type, the problem goes away.
I believe that the code is valid as it is. Or is it violating some Fortran language restriction that results in this surprising behaviour?
I try to introduce complex-valued array and variables in Fortran. For the following code
program
integer :: i , j !two dimensional real array
real(dp) :: m1(3,2)
complex(dp) :: a1(3,2),a2(3,2), c0, c1
!assigning some values to the array numbers
c0 = (1.0_dp, 0.0_dp)
c1 = (0.000000001_dp, 0.0_dp)
do i=1,3
do j = 1, 2
a1(i,j) = c0*i*j
end do
end do
do i=1,3
do j = 1, 2
a2(i,j) = c0*i*j + c1
end do
end do
do i=1,3
do j = 1, 2
if (dabs( dreal(a1(i,j)) - dreal(a2(i,j))) > 1.e-6 then
write (*,*), 'warning',i,j, a1(i,j), a2(i,j)
end if
end do
end do
write (*,*), a1(1,1), a2(1,1)
end program
ifort gives me
complex(dp) :: a1(3,2), a2(3,2)
-----------^
why complex(dp) requires compile-time constant and how to fix it? Thank you.
The kind parameter dp must be a constant, see Fortran - setting kind/precision of a variable at run time
However, you do not have the same problem as in the link, you did not try to define dp at all! First, you must use IMPLICIT NONE, it is absolutely necessary for safe programming and the biggest problem with your code. Then it will tell you that the type of dp is not declared.
You just define the kind constant in one of the usual ways, as a constant. The most simple is:
program main
!NECESSARY!
implicit none
integer, parameter :: dp = kind(1.d0)
Note that I named the program main, you have to name your program. Or just omit the program.
More about defining real kinds can be found at Fortran 90 kind parameter
Another note: Forget dabs() and dreal(). dabs() is an old remnant of Fortran 66 and dreal() is not standard Fortran at all. Just use abs and either dble() or better real( ,dp).
I write a simple demonstration code to present my question in a quick way. Here's the code, which can not be successfully built.
Main.f90
PROGRAM test
IMPLICIT NONE
INTEGER :: a
a = 1
CALL sub(a)
END PROGRAM
sub.f90
SUBROUTINE sub(a)
IMPLICIT NONE
INTEGER :: a
SELECT CASE(a)
CASE(1)
INTEGER :: b,c
b = a
c = a*2
CASE(2)
INTEGER :: b(4),c(4)
b(:) = a
c(:) = a*2
END SELECT
END SUBROUTINE
I tried to compile, but the error shows 'Unexpected data declaration statement' occurs in the subroutine file. Does it mean that I cannot declare argument type inside SELECT CASE structure? The problem is that I want to define the value of a in the main program and pass it into subroutine sub(a). The argument type of b and c should be decided by a, thus I cannot determine in advance. I also want to pass the value of b and c back to the main program, which I don't know how to do that. So how can I achieve this? Thanks.
So you you are actually asking how to return scalar or array from some subroutine, not how to declare construct-local variables. In that case consider using two separate subroutines. One version for scalars and one for arrays. You can overload them as a generic procedure under one name if you want.
Also think about ELEMENTAL, but if you use scalar a it won't work with the arrays.
If you still want to know how to declare local variables:
Variables can only be declared at the beginning of the procedure or at the beginning of a block. That is a Fortran 2008 feature supported in recent versions of the most common compilers (from PC compilers at least GNU and Intel).
SELECT CASE(a)
CASE(1)
BLOCK
INTEGER :: b,c
b = a
c = a*2
END BLOCK
The code as you write it is illegal, as you found out. Now some people have pointed to the 2008 feature of BLOCK statements, and if that's what you need, you can try that. But I'd like to learn more about what you want to do with this.
The very fact that you give them the same name suggests to me that you want to treat them the same way later on, which makes things really tricky.
Here are a few alternatives:
1) Use separate variables:
INTEGER :: b_scalar, c_scalar, b_array(4), c_array(4)
select case(a)
case(1)
b_scalar = a
c_scalar = 2*b_scalar
case(2)
b_array = a
c_array = 2*b_array
end select
2) Use allocatable arrays:
integer, dimension(:), allocatable :: b, c
select case(a)
case(1)
allocate(b(1), c(1))
case(2)
allocate(b(4), c(4))
end select
b = a
c = 2 * b
Now you have to remember that b and c are arrays, possibly with length 1. You have to treat them that way.
All of these have advantages and disadvantages. Without knowing why you are doing what you're doing, I don't really know how to best advise you.
As to your second question: The simple way to return them is as an INTENT(OUT) dummy argument. Here's a working example:
module mod_allocatable
contains
subroutine my_sub(a, b, c)
implicit none
integer, intent(in) :: a
integer, dimension(:), allocatable, intent(out) :: b, c
if (allocated(b)) deallocate(b)
if (allocated(c)) deallocate(c)
select case(a)
case(1)
allocate(b(1), c(1))
case(2)
allocate(b(4), c(4))
end select
b = a
c = 2 * b
end subroutine my_sub
end module mod_allocatable
program test_alloc
use mod_allocatable
implicit none
integer :: a
integer, allocatable, dimension(:) :: b, c
a = 1
call my_sub(a, b, c)
print *, "b is ", b
print *, "c is ", c
end program test_alloc
This is not overly elegant...
SUBROUTINE sub(a)
IMPLICIT NONE
INTEGER, INTENT(IN) :: a
INTEGER, DIMENSION(:), ALLOCATABLE :: b, c
SELECT CASE(a)
CASE(1)
IF(ALLOCATED(B)) THEN
IF(UBOUND(B)) .NE. 1) THEN
DEALLOCATE(B)
ALLOCATE(B(1))
ENDIF
ELSE
ALLOCATE(B(1))
ENDIF
IF(ALLOCATED(C)) THEN
IF(UBOUND(C)) .NE. 1) THEN
DEALLOCATE(c)
ALLOCATE(C(1))
ENDIF
ELSE
ALLOCATE(C(1))
ENDIF
b = a
c = a*2
CASE(2)
IF(ALLOCATED(B)) THEN
IF(UBOUND(B)) .NE. 4) THEN
DEALLOCATE(B)
ALLOCATE(B(4))
ENDIF
ELSE
ALLOCATE(B(4))
ENDIF
IF(ALLOCATED(C)) THEN
IF(UBOUND(C)) .NE. 4) THEN
DEALLOCATE(C)
ALLOCATE(C(4))
ENDIF
ELSE
ALLOCATE(C(4))
ENDIF
b(:) = a
c(:) = a*2
CASE(DEFAULT)
WRITE(*,*)'how did we get here?... a=',a
END SELECT
END SUBROUTINE Sub
How do you check values in Fortran like in Matlab? For example in the little program under, why does it show c=0 in main when it is c=36 in the subroutine testing? How do you make it so c=36 in the main program?
Can you call on the value c in some sort of way? I understand that in the main program the variable c is either undefined or has value 0, but is there a way to save the value of c in the subroutine so you can use it again in other subroutines, without calculating it again?
When the program is quite large it is handy to check values as you go.
program main
use test
implicit none
integer :: a,b,c
call testing(a,b)
write(*,*)'Test of c in main program',c
end program main
module test
implicit none
contains
subroutine testing(a,b)
integer :: a,b,c
a=2
b=3
c=(a*b)**a
write(*,*)'Value of c in subroutine',c
end subroutine testing
end module test
It is important to learn about scope in programming languages.
Every name (identifier) has only a limited range of validity.
If you declare some variable inside a subroutine
subroutine s
integer :: i
end subroutine
than the i is valid only in that subroutine.
If you declare a variable in a module
module m
integer :: i
contains
subroutines and functions
end module
then the i is valid inside all those subroutines and functions and also in all program units that use that module.
However, that does not mean that you should declare the variable in the module and just share it. That would be more or less a global variable. This is reserved only for certain cases, where that is necessary, but not for getting results out of your subprograms.
If your subroutine just computes something and you want to get the result of that computation you have two possibilities:
1. pass it as an additional argument, which will be defined by the subroutine
subroutine testing(a, b, c)
integer, intent(in) :: a, b
integer, intent(out) :: c
c = (a*b) ** a
end subroutine
you then call it as
call testing(2, 3, c)
print *, "result is:" c
2. Make it a function
function testing(a, b) result(c)
integer, intent(in) :: a, b
integer :: c
c = (a*b) ** a
end function
and then you can use directly
c = testing(2, 3)
print *, "result is:", c
or just
print *, "result is:", testing(2, 3)
This is the desired behaviour. Basically the calling routine should know nothing about the subroutine except for how to call it and what to get back. This is called encapsulation.
You can make variables accessible to the calling routine by declaring it in the module itself, and not in the subroutine:
module test
implicit none
integer :: c
contains
subroutine testing(a, b)
implicit none
integer :: a, b
a = 2
b = 3
c = (a*b) ** a
write(*, *) "Value of c in the subroutine: ", c
end subroutine testing
end module test
program main
use test
implicit none
integer :: a, b
call testing(a, b)
write(*, *) "Test of c in the main program: ", c
end program main
Note that you must not declare c in the main program, as it will get the variable from the module.
You could also use a COMMON block, but modules are far superior.
Is there a way to check whether aliasing occurs in a Fortran subroutine, or at least to tell the compiler to issue a warning?
Consider this (rather simplistic) example:
module alias
contains
subroutine myAdd(a, b, c)
integer,intent(in) :: a, b
integer,intent(inout) :: c
c = 0
c = a + b
end subroutine
end module
program test
use alias
integer :: a, b
a = 1 ; b = 2
call myAdd(a, b, b)
print *, b, 'is not 3'
end program
Here, the result is set to zero in the subroutine. If the same variable is given as input and output, the result is (obviously) wrong. Is there a way to capture this kind of aliasing at run-time or at compile-time?
Yes, gfortran will detect some aliasing with the compiler option -Waliasing, however, the arguments must have intents in and out. It won't work with your example because you have declared argument c as intent(inout). In this example you can simply change the intent to out since the input value of c is not used. They try the compiler option! gfortran outputs:
alias.f90:17.16:
call myAdd(a, b, b)
1
Warning: Same actual argument associated with INTENT(IN) argument 'b' and INTENT(OUT) argument 'c' at (1)
I don't know of options in g95 or gfortran to detect the aliasing error at compile or run time. The programmer is responsible for avoiding such an error. You can pass the intent(in) arguments as expressions to ensure that they are not changed within the subroutine, as shown below.
module alias
contains
subroutine myAdd(a, b, c)
integer,intent(in) :: a, b
integer,intent(inout) :: c
c = 0
c = a + b
end subroutine myadd
end module alias
program test
use alias, only: myadd
integer :: a, b
a = 1 ; b = 2
call myAdd((a),(b),b) ! parentheses around arguments a and b
print*, b,"is 3"
call myAdd(a, b, b)
print*, b,"is not 3"
end program test