17

Is there an intrinsic in Fortran that generates an array containing a sequence of numbers from a to b, similar to python's range()

>>> range(1,5)
[1, 2, 3, 4]
>>> range(6,10)
[6, 7, 8, 9]

?

Stefano Borini
  • 138,652
  • 96
  • 297
  • 431

3 Answers3

33

No, there isn't.

You can, however, initialize an array with a constructor that does the same thing,


program arraycons
  implicit none
  integer :: i
  real :: a(10) = (/(i, i=2,20, 2)/)
  print *, a
end program arraycons

janneb
  • 36,249
  • 2
  • 81
  • 97
  • Isn't that the same as in range() above? I don't see the difference (well, apart from the <= and <, and the fact that fortran isn't a dynamic language). Maybe I understood the question wrong (or the answer). – Rook Jan 14 '11 at 20:38
  • @Rook: It's an array constructor with an implied do loop, not an intrinsic. – janneb Jan 14 '11 at 22:27
  • A-ah, yes. I completely missed that part in the question. – Rook Jan 15 '11 at 00:54
  • @janneb thank you. Would you mind explaining which each argument to the constructor does? Am I right in thinking that this gives 2, 4, ... , 20? And is this more efficient than an explicit `DO` loop, or is it equivalent? – CJB Aug 24 '16 at 08:44
  • 2
    It is similar to a Python list comprehension. – Vladimir F Героям слава Nov 30 '17 at 13:13
  • This is truly list comprehension magic! I can even call a function in the constructor. OMG I'm gonna use this more often. Thank you so much! – Takeshi Jul 15 '18 at 06:43
6

If you need to support floats, here is a Fortran subroutine similar to linspace in NumPy and MATLAB.


! Generates evenly spaced numbers from `from` to `to` (inclusive).
!
! Inputs:
! -------
!
! from, to : the lower and upper boundaries of the numbers to generate
!
! Outputs:
! -------
!
! array : Array of evenly spaced numbers
!
subroutine linspace(from, to, array)
    real(dp), intent(in) :: from, to
    real(dp), intent(out) :: array(:)
    real(dp) :: range
    integer :: n, i
    n = size(array)
    range = to - from

    if (n == 0) return

    if (n == 1) then
        array(1) = from
        return
    end if


    do i=1, n
        array(i) = from + range * (i - 1) / (n - 1)
    end do
end subroutine

Usage:

real(dp) :: array(5)
call linspace(from=0._dp, to=1._dp, array=array)

Outputs the array

[0., 0.25, 0.5, 0.75, 1.]

Here dp is

integer, parameter :: dp = selected_real_kind(p = 15, r = 307) ! Double precision
Evgenii
  • 36,389
  • 27
  • 134
  • 170
2

It is possible to create a function that reproduces precisely the functionality of range in python:

module mod_python_utils
contains
  pure function range(n1,n2,dn_)
     integer,           intent(in) :: n1,n2
     integer, optional, intent(in) :: dn_
     integer, allocatable :: range(:)
     integer ::dn
     dn=1; if(present(dn_))dn=dn_
     if(dn<=0)then
        allocate(range(0))
     else
        allocate(range(1+(n2-n1)/dn))
        range=[(i,i=n1,n2,dn)]
     endif
  end function range
end module mod_python_utils

program testRange
   use mod_python_utils
   implicit none
   integer, allocatable :: v(:)
   v=range(51,70)
   print"(*(i0,x))",v
   v=range(-3,30,2)
   print"(*(i0,x))",v
   print"(*(i0,x))",range(1,100,3)
   print"(*(i0,x))",range(1,100,-3)
end program testRange

The output of the above is

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
-3 -1 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29
1 4 7 10 13 16 19 22 25 28 31 34 37 40 43 46 49 52 55 58 61 64 67 70 73 76 79 82 85 88 91 94 97 100

Notice that :

  1. the last line is empty: Fortran treats graciously zero-length arrays.
  2. allocated variables get automatically deallocated once out of scope.
Luca Argenti
  • 131
  • 4