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
Tablice o assumed-shape
Tablice assumed-rank
Tablice explicit-shape
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