この文書の URL は http://www.cc.kyoto-su.ac.jp/~mtkg/lecture/comp_B/2012/03.html です。

if 構文

数値の符号や大小関係など、様々な条件の違いに応じて、異なった処理を行いたいことがあります。 そのような場合には if 構文を使います。

if 文にはいくつかの変種がありますが、一番単純なのは次の形の if 文(論理 if 文)です。

if (論理式) 実行文

論理式はある条件が成り立つか判定するための式で、真 (.TRUE.) または偽 (.FALSE.) のどちらかの値を取ります。 論理式が真の場合は実行文が実行され、偽の場合は次の文に処理が移ります。

次のプログラムは、入力された値が正の場合だけ平方根を表示します。

program work0301
  implicit none
  real(8) :: dx

  write(*,*) 'Input a real:'
  read(*,*) dx

  if (dx > 0.0) write(*,*) sqrt(dx)

  stop
end program work0301

キーボードからいろいろな実数を与えて動作を確認してみましょう。

関係演算子

if(...) の中の論理式 dx > 0.0 によって dx が 0.0 より大きいか判定します。 > は「関係演算子」の一種です。 関係演算子は算術式の大小関係や等号・不等号の成立を判断するのに用いられ、次の6種類があります。

Fortran 90    FORTRAN77        意味
-----------------------------------------------------------------
e1 <  e2      e1 .LT. e2       e1 <  e2 ならば真、そうでなければ偽
e1 <= e2      e1 .LE. e2       e1 <= e2 ならば真、そうでなければ偽
e1 == e2      e1 .EQ. e2       e1 =  e2 ならば真、そうでなければ偽
e1 /= e2      e1 .NE. e2       e1 /= e2 ならば真、そうでなければ偽
e1 >= e2      e1 .GE. e2       e1 >= e2 ならば真、そうでなければ偽
e1 >  e2      e1 .GT. e2       e1 >  e2 ならば真、そうでなければ偽

等号は === が2つ)であることに注意しましょう。

関係演算子と型

関係演算子で結ばれた2つの算術式の型が異なる場合、より広い方の型に変換されてから比較が行われます。 例えば dx が real(8) 型の変数である場合、 dx > 1 では整数型の定数 1 が real(8) 型の定数 1.0d+0 に変換されてから大小が比較されます。

実数型はあくまでも近似値なので

if (dx == 1.0d+0) ...

といったプログラムは意図したように動かないことがあります。 (計算には誤差が含まれることがあるため dx が厳密に 1.0d+0 になるとは限りません。) 「dx が 1 に十分近い」といった、誤差を考慮した安全な比較方法を使いましょう。

ブロック if 文

入力された整数が偶数か奇数か判定して表示するプログラムを作成してみましょう。 偶奇の判定は 2 で割った余りを調べればよいですね。

program work0302
  implicit none
  integer :: nx

  write(*,*) 'Input an integer:'
  read(*,*) nx

  ! nx を 2 で割った余り mod(nx, 2) を調べて偶奇を判定する
  if (mod(nx, 2) == 0) then
     write(*,*) nx, ' is even'       ! 偶数
  else
     write(*,*) nx, ' is odd'        ! 奇数
  end if

  stop
end program work0302

ブロック if 文の構造

条件の成立・不成立に応じて異なる文を実行するにはブロック if 文を利用します。 ブロック if 文は次のような構造をしています。

if (論理式) then
   block1 (条件が真の場合の処理)
else
   block2 (条件が偽の場合の処理)
end if

論理式が真であれば block1 が実行され、偽であれば block2 が実行されます。

次に示すように block1 または block2 を省略することも可能です。 block2 を省略する場合は else も省略可能です(例3)。

!!! 例1
if (論理式) then
else
   block2 (条件が偽の場合の処理)
end if

!!! 例2
if (論理式) then
   block1 (条件が真の場合の処理)
else
end if

!!! 例3
if (論理式) then
   block1 (条件が真の場合の処理)
end if

else if 文

もっと複雑な条件判断の例として閏年 (leap year) の判定を考えてみましょう。 グレゴリオ暦では次の規則に従って閏年が設けられています。

  1. 西暦年が 4 で割り切れる年は閏年である。
  2. ただし、西暦年が 4 で割り切れる年のうち、100 で割り切れる年は平年とする。
  3. ただし、西暦年が 100 で割り切れる年のうち、400 で割り切れる年は閏年とする。

したがって、入力された西暦年が閏年か平年か判定するプログラムは次のようになります。

program work0303
  implicit none
  integer :: year

  write(*,*) 'Input year:'
  read(*,*) year

  if (mod(year, 400) == 0) then      ! 400 で割り切れるか
     write(*,*) 'leap year'
  else if (mod(year, 100) == 0) then ! 100 で割り切れるか
     write(*,*) 'common year'
  else if (mod(year, 4) == 0) then   !   4 で割り切れるか
     write(*,*) 'leap year'
  else
     write(*,*) 'common year'
  end if

  stop
end program work0303

処理の流れ

このプログラムでは、まず整数型の変数 year が 400 で割り切れるか判定します。 割り切れる場合は leap year と表示し、処理を end if に移して判定を終了します。 割り切れない場合は else if (mod(year, 100) == 100) then の行に処理が移され、100 で割り切れるかを判定します。 100 で割り切れるかどうかの判定は 400 で割り切れない場合だけ行われることに注意しましょう。

100 で割り切れる場合は common year と表示し、判定を終了します。 割り切れない場合は 4 で割り切れるか判定します。

4 で割り切れる場合は leap year と表示し、判定を終了します。 割り切れない場合は else に処理が移され common year と表示して終了します。

条件判断の順序

変数 year が 4 で割り切れるかどうかの判定から始めてしまうと、プログラムがかなり複雑になります(本日の課題)。 条件判断はもっとも厳しいものから行うとすっきり書けることが多いようです。 ただし、効率を重視し、比較の回数をなるべく減らしたいなら、もっとも起こりやすい条件から比較すべきでしょう。

練習問題

キーボードから整数を入力し、符号を判定せよ。 正の場合は positive, 負の場合は negative, 0 の場合は zero と表示すること。

program work0304
  implicit none
  integer :: nx

  write(*,*) 'Input an integer:'
  read(*,*) nx

  if (nx == 0) then
     write(*,*) 'zero'
  else if (nx > 0) then
     write(*,*) 'positive'
  else
     write(*,*) 'negative'
  end if

  stop
end program work0304

複合条件と論理演算子

こんどは月の数を入力してその月の日数を表示するプログラムを考えてみましょう。 ただし、閏年は考慮せず、2月は常に28日とします。

program work0305
  implicit none
  integer :: m

  write(*,*) 'Input month:'
  read(*,*) m

  if (m == 2) then
     write(*,*) 28
  else if (m == 4 .OR. m == 6 .OR. m == 9 .OR. m == 11) then
     write(*,*) 30
  else
     write(*,*) 31
  end if

  stop
end program work0305

論理演算子

最初の else if の内容 m == 4 .OR. m == 6 .OR. m == 9 .OR. m == 11 は「m が 4 または 6 または 9 または 11」という条件を表します。 演算子 .OR. は「または」(論理和)を表します。 「または」や「かつ」といった論理演算を行う演算子を「論理演算子」といいます。

論理演算子には次の5種類があります。 e1, e2 は論理式で dx > 1.0 といった論理式が入ります。

e1 .AND.  e2  論理積(かつ)   e1, e2 がともに真のときのみ真、他は偽
e1 .OR.   e2  論理和(または) e1, e2 がともに偽のときのみ偽、他は真
e1 .EQV.  e2  論理等価         e1, e2 がともに真またはともに偽のときのみ真、他は偽
e1 .NEQV. e2  論理非等価       e1 が真で e2 が偽、e1 が偽で e2 が真のときのみ真、他は偽
.NOT. e1      否定             e1 が真なら偽、偽なら真

例えば、「x が正かつ y が負」という条件は論理積演算子 .AND. を使って次のように表せます。

x > 0 .AND. y < 0

練習問題

キーボードから2つの実数 x, y を読み込み、点 (x, y) が第1象限にあれば OK、 なければ NG と表示するプログラムを作成せよ。

program work0306
  implicit none
  real(8) :: dx, dy

  write(*,*) 'Input x and y:'
  read(*,*) dx, dy

  if (dx > 0 .AND. dy > 0) then
     write(*,*) 'OK'
  else
     write(*,*) 'NG'
  end if

  stop
end program work0306

case 構文

Fortran 90 から case 構文という新しい条件判断の方法が追加されました。 case 構文は「値が(必ずしも連続でない)ある集合に含まれる」という条件を判定するのに適しています。 case 構文は if と else if を使って書き換えることもできます。 簡潔に記述できる方を選んで、適宜使い分けましょう。

例として、月の数を入力してその月の日数を表示するプログラムを考えてみましょう。 ただし、閏年は考慮せず、2月は常に28日とします。

program work0307
  implicit none
  integer :: m

  write(*,*) 'Input month:'
  read(*,*) m

  select case (m)
  case (1,3,5,7,8,10,12)
     write(*,*) 31
  case (4,6,9,11)
     write(*,*) 30
  case (2)
     write(*,*) 28
  case default
     write(*,*) 'Invalid month: ', m
  end select

  stop
end program work0307

if 文で書くよりすっきりしていると思いますが、いかがでしょうか?

case 構文の構造

case 構文は select case 文で開始され、end select 文で終わります。 書式は次のようになります。

select case (expr)
case selector1
   block1
case selector2
   block2
...
case default
   blockd
end select

expr には比較を行う変数(整数型、論理型、文字型のスカラー変数)が入ります。 実数型は許されないことに注意しましょう。

select case (expr) が実行されると expr の値を含む case セレクタが探され、一致する値がみつかると対応するブロックが実行されます。 セレクタの形式は値の「範囲」をコンマ , で区切ったものになります。 「範囲」の指定方法は

個別の値
下限値:上限値
下限値:
:上限値

のいずれかです。 以下に例を示します。

case (1)           ! m == 1
case (0:50)        ! m >= 0 .AND. m <= 50
case (:-1)         ! m <= -1
case (10:)         ! m >= 10
case (:-1, 2, 5:7) ! m <= -1 .OR. m == 2 .OR. (m >= 5 .AND. m <= 7)

case default

expr に一致するセレクタがみつからなかった場合は case default に対応するブロックが実行されます。 case default は省略可能で、その場合は何も実行されません。

練習問題

月の数を入力して季節を表示するプログラムを作成せよ。 ただし、月と季節の関係は

とする。 また、無効な月が入力された場合は invalid と表示すること。

!
! 季節の判定プログラム
!
program work0308
  implicit none
  integer :: m

  write(*,*) 'Input month:'
  read(*,*) m

  select case (m)
  case (3,4,5)
     write(*,*) 'spring'
  case (6,7,8)
     write(*,*) 'summer'
  case (9,10,11)
     write(*,*) 'fall'
  case (12,1,2)
     write(*,*) 'winter'
  case default
     write(*,*) 'invalid'
  end select

  stop
end program work0308

本日の課題

1. 大きい方の値

2つの整数を入力し、大きい方の値を表示するプログラムを作成せよ。

!
! 2つの数の最大値をみつける
!
program work0309
  implicit none
  integer :: i, j

  write(*,*) 'Input two integers:'
  read(*,*) i, j

  if (i > j) then
     write(*,*) i
  else
     write(*,*) j
  end if

  stop
end program work0309

2. 3つの数の最大値

3つの整数を入力し、最大値 (maximam value) を表示するプログラムを作成せよ。

!
! 3つの数の最大値をみつける
!
program work0310
  implicit none
  integer :: i, j, k
  integer :: m

  write(*,*) 'Input 3 integers:'
  read(*,*) i, j, k

  m = i
  if (j > m) m = j
  if (k > m) m = k

  write(*,*) 'max: ', m

  stop
end program work0310

3. 和暦の計算

キーボードから西暦年を入力し、対応する和暦年を表示するプログラムを作成せよ。 例えば、西暦2000年の場合は Heisei 12 と表示する。 西暦と和暦は次のように対応する。

西暦1868年     明治1年
西暦1912年     大正1年
西暦1926年     昭和1年
西暦1989年     平成1年

元号(明治、大正、昭和、平成)の判定には厳密には月日が必要である。 例えば、1912年は7月29日までが明治45年、7月30日以降が大正1年であるが、ここではプログラムを単純化するため、ある元号の最終年度は次の元号の1年としてよい。 つまり、1912年は大正1年、1926年は昭和1年などとする。 また、1868年以前の西暦年が入力された場合は Not implemented と表示せよ。

!
! 和暦の計算
!
program work0311
  implicit none
  integer :: chera

  write(*,*) 'Input the Christian Era (1868-2012):'
  read(*,*) chera

  select case (chera)
  case (1868:1911)
     write(*,*) 'Meiji',  chera - 1867
  case (1912:1925)
     write(*,*) 'Taisho', chera - 1911
  case (1926:1988)
     write(*,*) 'Showa',  chera - 1925
  case (1989:)
     write(*,*) 'Heisei', chera - 1988
  case default
     write(*,*) 'Not implemented'
  end select

  stop
end program work0311

チャレンジ問題

1. 平方根の近似計算

正の実数 x の平方根 √(x) はつぎの式から r5 として求められる。

r1 = (1 + x) / 2
r2 = (r1 + x / r1) / 2
r3 = (r2 + x / r2) / 2
r4 = (r3 + x / r3) / 2
r5 = (r4 + x / r4) / 2

実数 x の値をキーボードから読み込み、 r1, r2, r3, r4, r5 の各値を画面に出力するプログラムを作成せよ。

2. 曜日の計算

日曜日を 0、月曜日を 1、…、土曜日を 6 というように番号で表わすものとする。 このとき、今日の曜日を表わす番号 i と日数 n を読み込んで、n 日後の曜日を求め、番号で表示せよ。

3. 三角形の判定

次のような機能をもったプログラムを作成せよ。

ヒント: 「三角形の2辺の和は他の1辺の長さよりも長い」という条件を素直に使うと

a > 0 かつ b > 0 かつ c > 0 かつ a + b > c かつ b + c > a かつ c + a > b

が三角形成立の条件である。 これでは面倒なので、最初の3つの条件は、最小値を返す組込み関数 min(x1, x2, ...) を利用し、

min(a, b, c) > 0

で済ませることにする。 後半は次のように定義される ms を利用する。

m = (a + b + c) / 2
s = m * (m - a) * (m - b) * (m - c)

s が正であれば面積は √(s) で与えられる。 s が 0 以下であれば三角形ができないことを示す。