Operações sobre Elementos em Matrizes#
Há três abordagens para realizar operações sobre elementos em matrizes quando usando subrotinas e funções:
Procedimentos do tipo
elemental
matrizes explicit-shape
implementando a operação para vetores e escrevendo um empacotador simples de subrotinas (que use
reshape
internamente) para cada forma da matriz
Na primeira abordagem, usa-se a palavra-chave elemental
para criar uma função como essa:
real(dp) elemental function nroot(n, x) result(y)
integer, intent(in) :: n
real(dp), intent(in) :: x
y = x**(1._dp / n)
end function
Todos os argumentos (dentro e fora) devem ser escalares. Você pode então usar essa função com matrizes de qualquer tipo (compatível), por exemplo:
print *, nroot(2, 9._dp)
print *, nroot(2, [1._dp, 4._dp, 9._dp, 10._dp])
print *, nroot(2, reshape([1._dp, 4._dp, 9._dp, 10._dp], [2, 2]))
print *, nroot([2, 3, 4, 5], [1._dp, 4._dp, 9._dp, 10._dp])
print *, nroot([2, 3, 4, 5], 4._dp)
A saída será:
3.0000000000000000
1.0000000000000000 2.0000000000000000 3.0000000000000000 3.1622776601683795
1.0000000000000000 2.0000000000000000 3.0000000000000000 3.1622776601683795
1.0000000000000000 1.5874010519681994 1.7320508075688772 1.5848931924611136
2.0000000000000000 1.5874010519681994 1.4142135623730951 1.3195079107728942
No código acima, tipicamente n
é um parâmetro e x
é uma matriz de formato arbitrário, mas como pode ver, Fortran não se importa desde que a operação final faça sentido (se um argumento é uma matriz, então os outros argumentos devem ser matrizes do mesmo formato ou escalares). Se não for assim, você receberá um erro do compilador.
A palavra-chave elemental
implica na palavra-chave pure
, então o procedimento deve ser puro. Isso resulta que elemental procedures
só podem usar procedimentos pure
e não há efeitos colaterais.
Se o algoritmo de procedimento elementar puder ser feito mais rápido usando operações de matrizes internamente, ou se por alguma razão os argumentos deverão ser matrizes de formatos incompatíveis, então deve-se usar uma das duas outras abordagens. Pode-se fazer o nroot
operar em um vetor ou escrever um simples empacotador para os outros formatos das matrizes, p.ex.:
function nroot(n, x) result(y)
integer, intent(in) :: n
real(dp), intent(in) :: x(:)
real(dp) :: y(size(x))
y = x**(1._dp / n)
end function
function nroot_0d(n, x) result(y)
integer, intent(in) :: n
real(dp), intent(in) :: x
real(dp) :: y
real(dp) :: tmp(1)
tmp = nroot(n, [x])
y = tmp(1)
end function
function nroot_2d(n, x) result(y)
integer, intent(in) :: n
real(dp), intent(in) :: x(:, :)
real(dp) :: y(size(x, 1), size(x, 2))
y = reshape(nroot(n, reshape(x, [size(x)])), [size(x, 1), size(x, 2)])
end function
E use da seguinte forma:
print *, nroot_0d(2, 9._dp)
print *, nroot(2, [1._dp, 4._dp, 9._dp, 10._dp])
print *, nroot_2d(2, reshape([1._dp, 4._dp, 9._dp, 10._dp], [2, 2]))
O que será mostrado na tela como:
3.0000000000000000
1.0000000000000000 2.0000000000000000 3.0000000000000000 3.1622776601683795
1.0000000000000000 2.0000000000000000 3.0000000000000000 3.1622776601683795
Ou pode-se usar matrizes explicite-shape como apresentado a seguir:
function nroot(n, k, x) result(y)
integer, intent(in) :: n, k
real(dp), intent(in) :: x(k)
real(dp) :: y(k)
y = x**(1._dp / n)
end function
Use como se segue:
print *, nroot(2, 1, [9._dp])
print *, nroot(2, 4, [1._dp, 4._dp, 9._dp, 10._dp])
print *, nroot(2, 4, reshape([1._dp, 4._dp, 9._dp, 10._dp], [2, 2]))
A saída é a mesma que a anterior:
3.0000000000000000
1.0000000000000000 2.0000000000000000 3.0000000000000000 3.1622776601683795
1.0000000000000000 2.0000000000000000 3.0000000000000000 3.1622776601683795