Fortranでも三項演算子がしたい!(merge関数)


はじめに

昨今の多くの言語には三項演算子が実装されています.残念ながらFortranにはありませんが,merge関数を用いると三項演算子と同じ役割をしてくれます.

三項演算子とは

?: 演算子 - C# リファレンス | Microsoft Docs

構文
condition ? consequent : alternative

?:からなるこの演算子は,conditionTrueならばconsequentの値を,Falseならばalternativeの値を返します.(正確にはconsequentあるいはalternativeの式を評価して値を返します.)以下の例の様に簡単な条件分岐をより簡潔に記述できるメリットがあります.

例文
//statusへの代入の分岐を簡潔に書ける
status = condition ? "GOOD" : "BAD";

//上と同じ内容だが5行も必要
if(condition){
  status = "GOOD";
}else{
  status = "BAD";
}

merge関数とは

GNU Fortran Compiler: merge
2つの配列を併合させて新しく同じサイズの配列を作り出す関数です.新しい配列の各要素の値には元の配列の値を使用します.2つの配列のうちどちらの値を使用するかをlogicalの配列で指定します.以下に例文とその解説図を示します.

program main
  implicit none
  integer :: ifT(7), ifF(7),ans(7)
  logical :: mask(7), T=.true.,F=.false.

  ifT  = [  1,  2,  3,  4,  5,  6,  7]
  ifF  = [101,102,103,104,105,106,107]
  mask = [  T,  F,  T,  T,  F,  T,  F]

  ans = merge(ifT, ifF, mask)
  print'(7(i0,x))',ans
  ! 1 102 3 4 105 6 107

end program

merge関数による疑似的な三項演算子

merge関数は,引数に配列を与えた場合だけでなく,単一の値を与えた場合にも正常に働きます.これを用いて,疑似的に三項演算子として用いることができます.構文とサンプルのプログラムを示します.上述した一般的な三項演算子とは引数の順序が異なることに注意してください.

構文
merge(consequent, alternative, condition)
サンプルプログラム
program main
  implicit none
  integer :: ifT, ifF, ans
  logical :: flag

  flag = .true.
  ! flag = .false.
  ifT = 1
  ifF = 2
  ans = merge(ifT, ifF, flag)
  print'(I0)', ans
  ! flag==true  => ans==1
  ! flag==false => ans==2
end program

おまけ(ユーザー定義演算子を用いた実装)

三項演算子というのですから,関数ではなく演算子を使いたくないですか?ということでユーザー定義演算子を用いて三項演算子と同じ挙動を目指して実装してみました.
Fortranで定義できる演算子は単項演算子と二項演算子のみで,残念ながら三項演算子を定義することはできません.今回は?:を別に分けてそれぞれ2項演算子として定義することで実装しました.
とても使いにくそうな出来上がりになりました.

ユーザー定義演算子を用いた実装
module myModule
  implicit none

  interface operator( .hatena. )
    module procedure opHatena
  end interface
  interface operator( .TorF. )
    module procedure opTorF
  end interface

  type myType
    integer :: T, F
  end type

  contains
  type(myType) function opTorF( i1, i2 )
    implicit none
    integer, intent(in) :: i1, i2
    opTorF%T = i1
    opTorF%F = i2
  end function

  integer function opHatena( bool, TorF )
    implicit none
    logical, intent(in) :: bool
    type(myType), intent(in) :: TorF
    if(bool)then
      opHatena = TorF%T
    else
      opHatena = TorF%F
    end if
  end function
end module

program main
  use myModule
  implicit none
  integer :: ifT, ifF, ans
  logical :: flag

  flag = .false.
  ifT = 1
  ifF = 2
  ans = flag .hatena. (ifT.TorF.ifF)
  print'(I0)', ans
  ! flag==true  => ans==1
  ! flag==false => ans==2
end program