Matrizes#
Arrays are a central object in Fortran. The creation of dynamic sized arrays is discussed in the allocatable arrays section.
Para passar matrizes para processos há quatro formas disponíveis
matrizes assumed-shape
matrizes assumend-rank
matrizes explicit-shape
matrizes assumed-size
O modo indicado para passar matrizes para processos é por matrizes 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
Matrizes de dimensões maiores podem ser passadas de modo similar.
subroutine g(A)
real(dp), intent(in) :: A(:, :)
...
end subroutine g
A matriz é simplesmente passada
real(dp) :: r(5)
call f(r)
Nesse caso nenhuma cópia de matriz é feita, o que tem a vantagem de que o formato e tamanho da informação é automaticamente passada para frente e checada em tempo de compilação e opcionalmente em tempo de execução. Similarmente, caminhos de matrizes podem ser passada sem requisição de cópia da matriz mas como um descritor assumed-shape:
real(dp) :: r(10)
call f(r(1:10:2))
call f(r(2:10:2))
Essa deve ser sempre o meio padrão de passar matrizes para dentro e para fora de subrotinas. Evite passar matriz como pedaços inteiros, uma vez que ofusca a intenção real do código:
real(dp) :: r(10)
call f(r(:))
No caso de precisar matrizes genéricas para um procedimento, a funcionalidade assumed-rank introduzida na especificação Fortran 2018 pode ser usada
subroutine h(r)
real(dp), intent(in) :: r(..)
select rank(r)
rank(1)
! ...
rank(2)
! ...
end select
end subroutine h
A atual classificação pode ser consultada em tempo de execução usando o construto select rank
. Isso facilmente permite criar funções mais genéricas que precisam lidar com diferentes classificações de matrizes.
Matrizes explicit-shape pode ser útil para retornar dados para funções. A maioria de suas funcionalidades pode ser fornecidas por assumed-shape e matrizes assumed-rank, mas encontram uso frequente para servir de interface com C ou em processos Fortran legado, portanto serão discutidos brevemente aqui.
Para usar matrizes explicit-shape, a dimensão tem que ser passada explicitamente como um argumento fictício como no exemplo abaixo
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
Para matrizes de altas dimensões índices adicionais tem que ser passados.
subroutine g(m, n, A)
integer, intent(in) :: m, n
real(dp), intent(in) :: A(m, n)
...
end subroutine
As rotinas podem ser invocadas por
real(dp) :: r(5), s(3, 4)
call f(size(r), r)
call g(size(s, 1), size(s, 2), s)
Note que o formato não é checado, assim o código legal à seguir potencialmente produzirá resultados incorretos:
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
Nesse caso a configuração da memória é preservada mas o formato será mudado. Além disso, matrizes explicit-shape requerem memória contínua e irão criar matrizes temporárias no caso de caminhos de array não-contínuos forem passados.
Para retornar uma matriz vinda de uma função com uso 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
Finalmente, há matrizes assumed-size, que provém o menor tempo de compilação e checagem em tempo de execução e pode ser encontrado frequentemente em código legado. Eles devem ser evitados em favor de matrizes assumed-shape ou assumed-rank. Um argumento fictício de uma matriz assumed-size é identificada por um asterisco como última dimensão, isso desativa o uso dessa matriz com muitas funções intrínsecas, como size
ou shape
.
Para checar o tamanho e formato correto de uma matriz assumed-shape as funções intrínsecas size
e shape
podem ser usadas para consultas essas propriedades
if (size(r) /= 4) error stop "Incorrect size of 'r'"
if (any(shape(r) /= [2, 2])) error stop "Incorrect shape of 'r'"
Note que size
retorna o tamanho total de todas as dimensões. Para obter o formato de uma dimensão específica adicione essa dimensão como segundo argumento da função.
Matrizes podem ser inicializadas usando construtor para matrizes
integer :: r(5)
r = [1, 2, 3, 4, 5]
O construtor de matriz pode ser anotado com o tipo da matriz construída
real(dp) :: r(5)
r = [real(dp) :: 1, 2, 3, 4, 5]
Laços implícitos «do» podem também ser usados dentro de um construtor de matriz
integer :: i
real(dp) :: r(5)
r = [(real(i**2, dp), i = 1, size(r))]
Para que a matriz inicie com índice diferente de 1, faça:
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