配列に対する要素ごとの演算#

サブルーチンと関数を使用する場合、配列に対して要素ごとの演算を実行するには3つの方法があります

  • 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は最終的な演算が意味をなす限り(1つの引数が配列の場合、他の引数は同じ形状の配列またはスカラーのいずれかである必要がある)、気にしません。そうでない場合は、コンパイラエラーが発生します。

elementalキーワードはpureキーワードを意味するため、プロシージャは純粋である必要があります。その結果、elemental プロシージャpureプロシージャのみを使用でき、副作用はありません。

要素ごとのプロシージャアルゴリズムが内部で配列演算を使用して高速化できる場合、または何らかの理由で引数が互換性のない形状の配列である必要がある場合は、他の2つのアプローチを使用する必要があります。たとえば、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