Is there a Fortran equivalent of Python's for-else statement?
For example, the following sorts a list of numbers into different ranges. In Python, it is:
absth = [1, 2, 3, 4, 5]
vals = [.1, .2, .5, 1.2, 3.5, 3.7, 16.8, 19.8, 135.60]
counts = [0] * len(absth)
for v in vals:
for i, a in enumerate(absth):
if v < a:
counts[i] += 1
break
else:
counts[-1] += 1
In Fortran, this works the same:
do iv = 1, nvals
is_in_last_absth = .true.
do ia = 1, nabsth - 1
if vals(iv) < absth(ia) then
counts(ia) = counts(ia) + 1
is_in_last_absth = .false.
exit
end if
end do
if (is_in_last_absth) then
counts(nabsth) = counts(nabsth) + 1
end if
end do
But is there a way to not have to use is_in_last_absth
and replace it with something like the else
in Python?
If the question is specifically about binning a series of numbers, with absth
being the upper limits on each bin (bar the last which has no upper limit), then I'd probably write something like this:
PROGRAM test
IMPLICIT NONE
INTEGER :: ix
INTEGER, DIMENSION(5) :: absth = [1, 2, 3, 4, 5]
REAL, DIMENSION(9) :: vals = [.1, .2, .5, 1.2, 3.5, 3.7, 16.8, 19.8, 135.60]
INTEGER, DIMENSION(SIZE(absth)+1) :: bins
bins = 0
DO ix = 1, SIZE(bins)-1
bins(ix) = COUNT(vals<absth(ix))
END DO
bins(ix) = COUNT(vals)
bins = bins-EOSHIFT(bins,-1)
WRITE(*,*) 'bins = ', bins
! which writes 3 1 0 2 0 3
END PROGRAM test
then, when I was happy that the logic was correct I'd turn it into a function and add in some error checking.
If the question is more general, and asks what is the idiomatic Fortran (post 90) way to reproduce Python's for-else
structure, there are answers here to that too.
There is no direct equivalent to that python construct.
Note though, that early termination of a do loop with a counted loop control can be detected by examining the value of the do variable after the loop.
do iv = 1, nvals
do ia = 1, nabsth - 1
if (vals(iv) < absth(ia)) then
counts(ia) = counts(ia) + 1
exit
end if
end do
! If the loop terminates because it completes the iteration
! count (and not because the exit statement is executed) then
! the do variable is one step beyond the value it had
! during the last iteration.
if (ia == nabsth) then
counts(nabsth) = counts(nabsth) + 1
end if
end do
An exit statement can also jump out of more than just do loops:
do iv = 1, nvals
outer_block: block
do ia = 1, nabsth - 1
if (vals(iv) < absth(ia)) then
counts(ia) = counts(ia) + 1
exit outer_block
end if
end do
counts(nabsth) = counts(nabsth) + 1
end block outer_block
end do
and a cycle statement can cycle any do construct that the statement is nested within:
outer_loop: do iv = 1, nvals
do ia = 1, nabsth - 1
if (vals(iv) < absth(ia)) then
counts(ia) = counts(ia) + 1
cycle outer_loop
end if
end do
counts(nabsth) = counts(nabsth) + 1
end do outer_loop
GO TO statements allow arbitrary jumps. In particular, you write your for loop followed by the else block, followed by a labeled continue. Within the loop, if the condition is true then jump to the labeled continue. Otherwise the for loop will terminate normally, the else block will be executed and then the continue, exactly matching the semantics of python's for...else construct.
For example:
INTEGER nabsth, nvals
PARAMETER (nabsth=5, nvals=9)
INTEGER absth(nabsth), counts(nabsth)
REAL vals(nvals)
DATA absth/1, 2, 3, 4, 5/
DATA counts/0, 0, 0, 0, 0/
DATA vals/.1, .2, .5, 1.2, 3.5, 3.7, 16.8, 19.8, 135.60/
do iv = 1, nvals
do ia = 1, nabsth - 1
if (vals(iv) < absth(ia)) then
counts(ia) = counts(ia) + 1
goto 10
end if
end do
counts(nabsth) = counts(nabsth) + 1
10 continue
end do
WRITE (*,*), counts
end
Produces
3 1 0 2 3
Since the else
part of Python's for-else block is executed only when all the elements are processed, how about simply using the if
statement for the last element? For example,
program main
implicit none
integer i, n
print *, "n = ?" ; read(*,*) n
do i = 1, 10
if ( i <= n ) then
print *, i
else
exit
endif
if ( i == 10 ) print *, "reached the final index"
enddo
print *, "done"
end program
which probably corresponds to
n = int( input( "n = ? \n" ) )
for i in range( 1, 11 ):
if i <= n:
print( i )
else:
break
else:
print( "reached the final index" )
print( "done" )
Another way might be to use a labeled block
construct, for example:
program main
implicit none
integer i, n
print *, "n = ?" ; read(*,*) n
loop_i : block
do i = 1, 10
if ( i <= n ) then
print *, i
else
exit loop_i
endif
enddo
print *, "reached the final index"
endblock loop_i
print *, "done"
end program
According to Chap.20.1.7: "Exit from nearly any construct" in Modern Fortran Explained (by Metcalf et al) and also the F2008 Standards Chap.8.1.10 (obtained from here ), it is OK to exit from any labeled construct like block
, if
, associate
etc, but we may need a relatively new compiler (gfortran-6 worked for me). The IBM man page for exit is also useful.
To the best of my knowledge, Python is the only (or one of the very few) languages that has a for-else statement. No, Fortran does not have it.
Sometimes GOTO is good. A WHERE ELSEWHERE may be useful...
do iv = 1, nvals
is_in_last_absth = .true.
Mask = .FALSE.
Mask(1:(nabsth - 1)) = .TRUE.)
Mask2 = .FALSE.
WHERE(MASK)
WHERE( vals(iv) < absth)
mask2 = .TRUE.
ENDWHERE
WHERE(Mask2)
Count = Count + 1
ELSE
LastCount = LastCount + 1
ENDWHERE
END WHERE
end do
count(2:(n-1)) = count(2:(n-1))+ lastcount(1:(n))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.