数组的逐元操作#

使用子例程和函数时,可以通过三种方法对数组执行逐元操作:

  • 逐元(elemental) 过程

  • 显式形状 数组

  • 实现向量的操作并为每个数组形状编写简单的包装子例程(在内部使用 reshape

在第一种方法中,使用 elemental 关键字来创建这样的函数:

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

所有参数(输入和输出)必须是标量。然后,你可以将此函数与任何(兼容)形状的数组一起使用,例如:

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)

输出将是:

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

在上面,通常 n 是一个参数,而 x 是任意形状的数组,但是正如你所看到的,Fortran 并不关心,只要最终的操作有意义(如果一个参数是一个数组,那么其它参数必须是相同形状的数组或标量)。如果没有,你将收到编译器错误。

elemental 关键字隐含了 pure 关键字,因此过程必须是纯的。这表明逐元过程只能使用过程,没有副作用。

如果使用内部数组操作可以使基本过程算法更快,或者如果由于某些原因参数必须是不兼容形状的数组,那么应该使用其它两种方法。可以使 nroot 对向量进行操作,并为其它数组形状编写一个简单的包装器,例如:

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

并按如下方式使用:

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]))

这将会打印:

3.0000000000000000
1.0000000000000000        2.0000000000000000        3.0000000000000000        3.1622776601683795
1.0000000000000000        2.0000000000000000        3.0000000000000000        3.1622776601683795

或者可以使用 显式形状 数组,如下所示:

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

使用如下:

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]))

输出与之前相同:

3.0000000000000000
1.0000000000000000        2.0000000000000000        3.0000000000000000        3.1622776601683795
1.0000000000000000        2.0000000000000000        3.0000000000000000        3.1622776601683795