情報学入門(データ演習) / 第 6 章

分岐(2)

ブロック If・Select Case ── 何通りにも分ける

この章のねらい

1. ブロック If 文

第 5 章の 1 行の If では、条件が真のときに文を 1 つしか書けません。 複数の文を実行したい、3 通り以上に分けたい ── そのための形がブロック If 文です。

If <条件式1> Then
    <ブロック1>
ElseIf <条件式2> Then
    <ブロック2>
    ︙
ElseIf <条件式n-1> Then
    <ブロックn-1>
Else
    <ブロックn>
End If

いちばん大事な性質。 元資料は書式を示すだけですが、ここを押さえないと必ずつまずきます ── ElseIf は上から順に調べ、最初に真になったところだけを実行して、あとは飛ばします。 つまり 2 番目の条件は「1 番目が偽だった」ことが前提になっています。 だから ElseIf x >= 70 と書くだけで、 「70 以上かつ 80 未満」の意味になるのです(上に x >= 80 があるため)。

2. 点数を判定する(元資料 6 枚目の課題)

点数を入力し、70〜100 点であれば背景をで "Good"、 50〜69 点であれば背景をシアンで "Fine"、 0〜49 点であれば背景をで "Bad" を表示しなさい。

0〜49Bad(赤)
50〜69Fine(シアン)
70〜100Good(緑)
Sub Macro1()
    Dim ten As Integer

    ten = Cells(1, 2).Value

    If ten >= 70 Then
        Cells(2, 2) = "Good"
        Cells(2, 2).Interior.Color = RGB(198, 239, 206)   ' 緑
    ElseIf ten >= 50 Then
        Cells(2, 2) = "Fine"
        Cells(2, 2).Interior.Color = RGB(183, 230, 240)   ' シアン
    Else
        Cells(2, 2) = "Bad"
        Cells(2, 2).Interior.Color = RGB(255, 199, 206)   ' 赤
    End If

    Cells(2, 1) = "判定"
End Sub

上限を書いていないことに注目してください。 「70〜100 点」なのに If ten >= 70 Then だけです。 100 点までしか来ないことが分かっていれば、これで足ります。 そして ElseIf ten >= 50 は、 すでに「70 未満」であることが確定しているので 50〜69 になります

2.1 条件に穴があいている

上のプログラムには問題があります。 B1 に 150−20 を入れて実行してみてください。

150 → Good−20 → Bad1000 → Good

課題の条件は「0〜100 点」の範囲しか決めていないので、 それ以外の値が来ると間違った判定を、エラーも出さずに返します。 元資料 10 枚目が、まさにこの点を課題にしています。

このプログラムに、条件
・101 点以上の場合 ⇒ Error(赤)
・マイナスの場合 ⇒ Error(赤)
以上 2 つの条件を追加して書き直せ。

Sub Macro2()
    Dim ten As Integer

    ten = Cells(1, 2).Value

    ' 先に「ありえない値」をはじく
    If ten > 100 Or ten < 0 Then
        Cells(2, 2) = "Error"
        Cells(2, 2).Interior.Color = RGB(255, 0, 0)
    ElseIf ten >= 70 Then
        Cells(2, 2) = "Good"
        Cells(2, 2).Interior.Color = RGB(198, 239, 206)
    ElseIf ten >= 50 Then
        Cells(2, 2) = "Fine"
        Cells(2, 2).Interior.Color = RGB(183, 230, 240)
    Else
        Cells(2, 2) = "Bad"
        Cells(2, 2).Interior.Color = RGB(255, 199, 206)
    End If

    Cells(2, 1) = "判定"
    Cells(4, 1) = "150 や -20 を入れて確かめてください"
End Sub

おかしな値のチェックは、いちばん先に書く。 これは分岐を書くときの定石です。 もし ten > 100 の判定を最後に置いたら、 150 は先に ten >= 70 に引っかかって "Good" になってしまいます ── ElseIf は上から順なので、条件を書く順番が意味を持ちます。

実務では、この「入ってくる値が想定どおりか確かめる」処理を 入力の検証(バリデーション)と呼び、 プログラムの分量の何割かを占めます。 正常な場合を書くのは簡単で、異常な場合を数え上げるのが難しいのです。

3. IIf 関数

IIf ( <条件式>, <式1>, <式2> )

<式1> は条件式がの場合、<式2> はの場合の値。
[例] a = IIf( x >= 30, "1.タクシーに乗る。", "バスに乗る。")

名前は「Immediate If」(すぐその場で判定する If)から来ています。 If 文は「文」ですが、IIf は「関数」── つまり値を返します。 だから代入式の右側やセルへの代入の中に、そのまま書けます。

IfIIf 関数
正体(処理の流れを分ける)関数(値を返す)
分けられる数何通りでも(ElseIf2 通りだけ
中に複数の文書ける書けない
向いている場面処理そのものを分けたいとき値を 2 択で選ぶだけのとき

IIf には落とし穴があります。 関数なので、真の側と偽の側の両方が評価されます (使われない側も計算されます)。 たとえば IIf(b <> 0, a / b, 0) と書くと、 b が 0 のときも a / b が計算されてエラーになります。 第 5 章 §4.4 の AndOr と同じ事情です。 割り算や関数呼び出しを IIf の中に入れるときは注意してください。

Sub Macro3()
    Dim x As Integer
    Dim ten As Integer

    x = Cells(1, 2).Value
    ten = Cells(3, 2).Value

    ' 2 択なら IIf のほうが短い
    Cells(2, 1) = "手段"
    Cells(2, 2) = IIf(x >= 30, "1.タクシーに乗る。", "バスに乗る。")

    ' 合否も 2 択
    Cells(4, 1) = "合否"
    Cells(4, 2) = IIf(ten >= 60, "合格", "不合格")

    ' 3 通り以上は IIf を重ねられるが、読みにくい(悪い例)
    Cells(6, 1) = "IIf を重ねた例"
    Cells(6, 2) = IIf(ten >= 70, "Good", IIf(ten >= 50, "Fine", "Bad"))
    Cells(7, 1) = "↑ 読みにくい。3 通り以上は If 文か Select Case を使う"
End Sub

4. Select Case 文

Select Case <評価式>
  Case <式1>
    <ブロック1>
  Case <式2>
    <ブロック2>
    ︙
  Case Else
    <ブロックn>
End Select

1 つの値を、いくつもの場合に振り分けるときの書き方です。 ElseIf と同じことができますが、 「何を見て分けているのか」が 1 行目にはっきり書かれるのが利点です。

Case の書き方は 4 通りあります。元資料は Case <式> の形だけを示していますが、実際にはこれだけ書けます。

書き方意味
Case 3値が 3 のとき
Case 3, 5, 73 か 5 か 7 のとき(コンマで並べる)
Case 70 To 10070 以上 100 以下のとき(範囲)
Case Is >= 6060 以上のとき(比較。Is が必要)
Case Elseどれにも当てはまらないとき
Sub Macro4()
    Dim ten As Integer
    Dim hantei As String
    Dim iro As Long

    ten = Cells(1, 2).Value

    Select Case ten
        Case Is > 100          ' 101 点以上
            hantei = "Error": iro = RGB(255, 0, 0)
        Case Is < 0            ' マイナス
            hantei = "Error": iro = RGB(255, 0, 0)
        Case 70 To 100
            hantei = "Good":  iro = RGB(198, 239, 206)
        Case 50 To 69
            hantei = "Fine":  iro = RGB(183, 230, 240)
        Case 0 To 49
            hantei = "Bad":   iro = RGB(255, 199, 206)
        Case Else
            hantei = "?":     iro = RGB(217, 217, 217)
    End Select

    Cells(2, 1) = "判定"
    Cells(2, 2) = hantei
    Cells(2, 2).Interior.Color = iro

    Cells(4, 1) = "範囲がそのまま書けるので、条件の抜けを見つけやすい"
End Sub

こちらの版は、範囲が目で見て確かめられます。 70 To 10050 To 690 To 49 と並べば、 すき間や重なりがないかがひと目で分かります。 ElseIf ten >= 50 という書き方では「上限は上の条件で決まっている」と 頭の中で補わなければなりません。読む人の負担が違います。

どちらを使うか
向いている場合
If 〜 ElseIf 条件ごとに見る変数が違うとき/複雑な条件式 If gakusei And nen <= 25 Then … ElseIf kane > 5000 Then
Select Case 1 つの値を場合分けするとき 点数の判定、曜日、メニュー番号、都道府県コード

5. 2 次方程式の解(元資料 15 枚目)

ax2bxc = 0 の解は

x b ± √(b2 − 4ac) 2a

ここで √ の中身 Db2 − 4ac判別式といい、 この符号で解の様子が変わります ── 分岐の練習にうってつけの題材です。

判別式 D
D > 0異なる 2 つの実数解
D = 0重解(1 つ)
D < 0実数解なし(虚数解)

元資料が扱っていない、もう 1 つの場合。 上の公式には a で割る操作が入っています。 つまり a = 0 のときは使えません(0 で割るエラーになります)。 そして a = 0 なら、それはもう 2 次方程式ではなく bxc = 0 の 1 次方程式です。 さらに b も 0 なら、c = 0 かどうかで「すべての x」か「解なし」に分かれます。 「ありえない入力」をはじくのではなく、意味のある別の場合として扱う例です。

Sub Macro5()
    Dim a As Double, b As Double, c As Double
    Dim D As Double
    Dim x1 As Double, x2 As Double

    a = Cells(1, 2).Value
    b = Cells(2, 2).Value
    c = Cells(3, 2).Value

    Cells(5, 1) = "式"
    Cells(5, 2) = a & "x^2 + " & b & "x + " & c & " = 0"

    If a = 0 Then
        ' 2 次方程式ではない
        If b = 0 Then
            If c = 0 Then
                Cells(6, 1) = "すべての x が解"
            Else
                Cells(6, 1) = "解なし(0 = " & c & " は成り立たない)"
            End If
        Else
            Cells(6, 1) = "1 次方程式です"
            Cells(7, 1) = "x"
            Cells(7, 2) = -c / b
        End If
    Else
        D = b ^ 2 - 4 * a * c
        Cells(6, 1) = "判別式 D"
        Cells(6, 2) = D

        If D > 0 Then
            x1 = (-b + Sqr(D)) / (2 * a)
            x2 = (-b - Sqr(D)) / (2 * a)
            Cells(7, 1) = "異なる 2 つの実数解"
            Cells(8, 1) = "x1": Cells(8, 2) = x1
            Cells(9, 1) = "x2": Cells(9, 2) = x2
        ElseIf D = 0 Then
            x1 = -b / (2 * a)
            Cells(7, 1) = "重解"
            Cells(8, 1) = "x":  Cells(8, 2) = x1
        Else
            Cells(7, 1) = "実数解なし(虚数解)"
            Cells(8, 1) = "実部":   Cells(8, 2) = -b / (2 * a)
            Cells(9, 1) = "虚部 ±": Cells(9, 2) = Sqr(-D) / (2 * a)
        End If
    End If
End Sub

試す値の例。 a=1, b=-5, c=6 → 2 つの解(2 と 3)/ a=1, b=-2, c=1 → 重解(1)/ a=1, b=0, c=1 → 虚数解(±i)/ a=0, b=2, c=-4 → 1 次方程式(2)/ a=0, b=0, c=5 → 解なし/ a=0, b=0, c=0 → すべての x6 通りすべてを試してみてください。 分岐を書いたら、すべての枝を 1 度は通すのがテストの基本です。

D = 0 という比較は、実は危ないです。 第 3 章で扱ったとおり、小数は 2 進数で正確に表せないことがあります (a = 0.1 のような値を入れると、 本来 0 になるはずの判別式が 0.0000000001 のような値になり、 重解のはずが「異なる 2 つの実数解」と判定されることがあります)。 実務では If Abs(D) < 0.000001 Then のように許容誤差つきで比べます。 上のプログラムで a=0.1, b=-0.6, c=0.9 を入れて試してみてください。

6. まとめ

  1. ブロック If は If 〜 ElseIf 〜 Else 〜 End If。複数の文を書ける
  2. ElseIf は上から順に調べ、最初に真になった枝だけを実行する。 だから順番に意味がある
  3. ありえない値のチェックは、いちばん先に書く
  4. IIf関数で値を返す。2 択専用。両側が評価されるので割り算に注意
  5. Select Case1 つの値を振り分ける。 To で範囲、, で並列、Is で比較
  6. 範囲を並べて書けるので、条件のすき間や重なりを見つけやすい
  7. 分岐を書いたらすべての枝を 1 度は通す。小数の = 0 比較は許容誤差で

章末問題

付録:元資料からの修正一覧

もとにしたのは「情報学入門(VBA)135-2019」全 19 枚です。

元資料この教材種別
ElseIf上から順に調べ、最初に真になった枝だけ実行するという 肝心の性質が書かれていない 明示した。「70〜100 点」なのに上限を書かなくてよい理由がここにある不足
Select CaseCaseCase <式> の形だけ Case 3, 5, 7Case 70 To 100Case Is >= 60 の 4 通りを表にした不足
If 文と Select Case使い分けの指針がない 「条件ごとに見る変数が違う → If」「1 つの値を振り分ける → Select Case」として表にした不足
IIf は書式と例だけで、If 文との違い(文か関数か)に触れていない 比較表を追加。さらに両側が評価される落とし穴(0 で割る危険)を明記不足
IIf の例が資料 130(第 5 回)と 135(第 6 回)の両方にある。 しかも 130 では x = IIf(...)、135 では a = IIf(...) と 代入先の変数名が違う(同じ例のはずが不統一) この章に集約し、135 の a = IIf(...) を採った重複
2 次方程式の解で a = 0 の場合(0 で割る)に触れていない 1 次方程式・解なし・すべての x を含めた6 通りの場合分けにした不足
判別式 D= 0 で比較している 小数では危ない(第 3 章の誤差)。許容誤差つきの比較を追記し、実例で試せるようにした不足
課題(101 点以上・マイナスを Error にする)の答えと考え方が資料にない 「おかしな値のチェックは先に書く」という定石として示し、実行できる形で載せた不足

元資料で正しかったこと。 ブロック If の書式、IIf の書式と説明 (「条件式が真の場合は式1 を返し、偽の場合は式2 を返す」)、 Select Case の書式、点数判定の課題設定(緑 Good・シアン Fine・赤 Bad)、 そして「101 点以上とマイナスを追加して書き直せ」という課題の立て方 ── いずれも適切です。とくに最後の課題は、条件の穴に気づかせるよい設計なので、 この章の中心に据えました。