Tablice#

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

Istnieją cztery sposoby, aby przekazać tablicę do procedury

  1. Tablice o assumed-shape

  2. Tablice assumed-rank

  3. Tablice explicit-shape

  4. Tablice assumed-size

Preferowaną metodą przekazywania tablic do procedur jest metoda tablic assumed-shape (o założonym kształcie)

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

Wielowymiarowe tablice mogą być przekazywane w podobny sposób.

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

Tablica jest prosto przekazywana przez

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

W tym przypadku nie jest tworzona żadna kopia tablicy, co sprawia, że informacje o kształcie i rozmiarze mogą być automatycznie przekazywane i sprawdzane przy kompilowaniu lub opcjonalnie w trakcie wykonywania programu. Podobnie, postępy w tablicach nie wymagają tworzenia kopii tablicy, ale mogą być przekazywane jako deskryptor assumed-shape:

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

To powinna być podstawowa forma przekazywania tablic do i z podprogramów. Unikaj przekazywania tablic jako całych wycinków, ponieważ utrudnia to zobaczenie rzeczywistych celi kodu:

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

W przypadku, gdy do procedury należy przekazać bardziej ogólne tablice, można wykorzystać funkcjonalność assumed-rank wprowadzoną w standardzie Fortran 2018

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

Rzeczywistą rangę można sprawdzić w trakcie wykonywania programu, używając konstrukcji select rank. Pozwala to łatwo tworzyć bardziej ogólne funkcje, będą obsługiwać tablice różnych rang.

Tablice explicit-shape (o jawnym kształcie) mogą być przydatne do zwracania danych z funkcji. Większa część ich funkcjonalności zostanie opisana w sekcjach o tablicach assumed-shape oraz assumed-rank jednak są one często używane do współdziałania z C i starszymi procedurami Fortran, dlatego zostaną tu krótko przedstawione.

Aby użyć tablic explicit-shape wymiar musi zostać przekazany jawnie jako argument fikcyjny, tak jak przestawiono w poniższym przykładzie

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

W przypadku tablic wielowymiarowych konieczne jest przekazanie dodatkowych indeksów.

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

Procedury te można wywołać za pomocą

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

Zwróć uwagę, że kształt tablic nie jest sprawdzany, więc następujący przykład nie jest poprawnym kodem i może potencjalnie skutkować nieprawidłowymi rezultatami:

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

W tym przypadku układ pamięci jest zachowany, jednak kształt zmieniony. Dodatkowo, tablice explicit-shape wymagają ciągłego dostępu do pamięci i utworzą tymczasowe tablice w przypadku przekazania nieciągłych wycinków tablic.

Aby zwrócić tablicę z funkcji explicit-shape należy użyć

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

Na koniec, istnieją również tablice assumed-size, które zapewniają najmniejsze sprawdzanie w czasie kompilacji i wykonywania programu i mogą być często spotykane w starszym kodzie. Należy ich unikać na rzecz tablic assumed-shape lub assumed-rank. Fikcyjny argument tablicy assumed-size jest identyfikowany gwiazdką jako ostatni wymiar, co uniemożliwia użycie tej tablicy z wieloma funkcjami wewnętrznymi, takimi jak size lub shape.

Aby sprawdzić poprawny rozmiar i kształt tablicy assumed-shape, mogą zostać użyte wewnętrzne funkcje size i shape, które zapewnią te właściwości

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

Należy pamiętać, że size zwróci całkowity rozmiar wszystkich wymiarów. Aby otrzymać kształt konkretnego wymiaru należy przekazać go do funkcji jako drugi argument.

Tablice można inicjować przez użycie konstruktora tablicy

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

Konstruktor tablicy może zawierać adnotację określającą typ tworzonej tablicy

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

Pętle „do loop” mogą być używane również wewnątrz konstruktora tablicy

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

Aby rozpocząć tablicę z indeksem innym niż 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