Массивы#

Arrays are a central object in Fortran. The creation of dynamic sized arrays is discussed in the allocatable arrays section.

Для передачи массивов в процедуры доступны четыре способа

  1. assumed-shape массивы (предполагаемой формы)

  2. assumed-rank массивы (предполагаемой размерности)

  3. explicit-shape массивы (явной формы)

  4. assumed-size массивы (предполагаемого размера)

Предпочтительным способом передачи массивов в процедуры является передача массивов в виде assumed-shape массивов (предполагаемой формы)

subroutine f(r)
  real(dp), intent(out) :: r(:)
  integer :: n, i
  n = size(r)
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end subroutine f

Массивы большей размерности могут быть переданы аналогичным образом.

subroutine g(A)
  real(dp), intent(in) :: A(:, :)
  ...
end subroutine g

Имя массива просто передаётся как аргумент

real(dp) :: r(5)
call f(r)

В этом случае копирование массива не производится, что имеет то преимущество, что информация о форме и размере автоматически передаётся и проверяется при компиляции и, по желанию, во время выполнения. Аналогично, строки массива могут передаваться без необходимости копирования массива, а как дескриптор assumed-shape (предполагаемой формы):

real(dp) :: r(10)
call f(r(1:10:2))
call f(r(2:10:2))

Это всегда должно быть вашим стандартным способом передачи массивов в подпрограммы и из подпрограмм. Избегайте передачи массивов как целых фрагментов, так как это запутывает реальный замысел кода:

real(dp) :: r(10)
call f(r(:))

В случае, если процедуре необходимо передавать более общие массивы, можно использовать функциональность assumed-rank (предполагаемой размерности), введённую в стандарте Fortran 2018

subroutine h(r)
  real(dp), intent(in) :: r(..)
  select rank(r)
  rank(1)
  ! ...
  rank(2)
  ! ...
  end select
end subroutine h

Фактическая размерность может быть запрошена во время выполнения с помощью конструкции select rank. Это позволяет легко создавать более универсальные функции, которым приходится иметь дело с массивами разной размерности.

Explicit-shape массивы (явной формы) могут быть полезны для возврата данных из функций. Большая часть их функциональности может быть предоставлена assumed-shape массивами (предполагаемой формы) и assumed-rank массивами (предполагаемой размерности), но они часто используются для взаимодействия с языком C или в унаследованных процедурах Fortran, поэтому здесь они будут рассмотрены кратко.

Чтобы использовать explicit-shape массивы (явной формы), размерность должна быть передана в явном виде как фиктивный аргумент, как в примере ниже

subroutine f(n, r)
  integer, intent(in) :: n
  real(dp), intent(out) :: r(n)
  integer :: i
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end subroutine

Для массивов больших размерностей необходимо передавать дополнительные индексы.

subroutine g(m, n, A)
  integer, intent(in) :: m, n
  real(dp), intent(in) :: A(m, n)
  ...
end subroutine

Эти процедуры могут быть вызваны как

real(dp) :: r(5), s(3, 4)
call f(size(r), r)
call g(size(s, 1), size(s, 2), s)

Обратите внимание, что форма массива не проверяется, поэтому следующий код будет легальным и потенциально может дать неверные результаты:

real(dp) :: s(3, 4)
call g(size(s), 1, s)  ! s(12, 1) in g
call g(size(s, 2), size(s, 1), s)  ! s(4, 3) in g

В этом случае схема памяти сохраняется, но форма изменяется. Кроме того, explicit-shape массивы (явной формы) требуют непрерывной памяти и будут создавать временные массивы в случае, если будут переданы несмежные строки массива.

Чтобы вернуть массив из функции как explicit-shape массив (явной формы), используйте

function f(n) result(r)
  integer, intent(in) :: n
  real(dp) :: r(n)
  integer :: i
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end function

Наконец, существуют assumed-size (предполагаемого размера) массивы, которые обеспечивают наименьшую проверку во время компиляции и выполнения и часто встречаются в устаревшем коде. Их следует избегать и вместо них использовать assumed-shape массивы (предполагаемой формы) или assumed-rank массивы (предполагаемой размерности). Фиктивный аргумент assumed-size массива (предполагаемого размера) обозначается звёздочкой в качестве последнего измерения, что запрещает использование такого массива со многими встроенными функциями, такими как size или shape.

Для проверки правильности размера и формы assumed-shape массива (предполагаемой формы) можно использовать встроенные функции size и shape для запроса этих свойств

if (size(r) /= 4) error stop "Incorrect size of 'r'"
if (any(shape(r) /= [2, 2])) error stop "Incorrect shape of 'r'"

Обратите внимание, что size возвращает общий размер всех измерений. Чтобы получить форму конкретного измерения, добавьте его номер в качестве второго аргумента в функции.

Массивы могут быть инициализированы с помощью конструктора массивов

integer :: r(5)
r = [1, 2, 3, 4, 5]

Конструктор массива может быть аннотирован типом конструируемого массива

real(dp) :: r(5)
r = [real(dp) :: 1, 2, 3, 4, 5]

Неявные циклы do можно использовать и внутри конструктора массива

integer :: i
real(dp) :: r(5)
r = [(real(i**2, dp), i = 1, size(r))]

Для того чтобы массив начинался с индекса, отличного от 1, сделайте следующее:

subroutine print_eigenvalues(kappa_min, lam)
  integer, intent(in) :: kappa_min
  real(dp), intent(in) :: lam(kappa_min:)

  integer :: kappa
  do kappa = kappa_min, ubound(lam, 1)
    print *, kappa, lam(kappa)
  end do
end subroutine print_eigenvalues