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

繰り返し(1)

For 〜 Next と While 〜 Wend ── 同じことを何度も

この章のねらい

1. 繰り返しとは(薬を飲む)

元資料 6〜8 枚目の例です。「薬を 7 錠飲む」という手順を流れ図で書きます。

始め
薬を 1 錠飲む ①
7 錠飲んだか? ②
↓ 偽
薬を 1 錠のむ ③
薬のカウントを増やす ④
↑ ②へ戻る
終了(②が真のとき)

この流れ図には「薬を 1 錠飲む」が 2 か所(①と③)にあります。 元資料 8 枚目のトレースを見ると、①で 1 錠飲んでから②で判断し、 ③でもう 1 錠飲む ── という動きになっています。7 錠になるので答えは合いますが、 同じ処理を 2 か所に書くのは、繰り返しの形が整っていない印です。

整えると、飲む処理は 1 か所で済みます。

始め
カウント = 0
カウント < 7 か?
↓ 真
薬を 1 錠飲む/カウントを増やす
↑ 判断へ戻る
終了(偽になったとき)

なぜこれが大事か。 同じ処理が 2 か所にあると、直すときに片方だけ直して不具合になります (「1 錠」を「2 錠」に変えるとき、③だけ変えたら 1 錠目が取り残される)。 繰り返しの目的は「回数を減らすこと」ではなく「同じことを 1 か所にまとめること」です。

2. For 〜 Next 文

回数が決まっている繰り返しに使います(元資料 4・5 枚目)。

For 制御変数 = 初期値 To 最終値 [Step 増分値]
    ブロック
Next 制御変数

Sub Macro1()
    Dim i As Integer
    Dim s As Integer
    Dim N As Integer

    N = 5
    s = 0

    For i = 1 To N
        s = s + i
        ' 途中の様子をシートに書き出す(自分で追える)
        Cells(i, 1) = "i = " & i
        Cells(i, 2) = "s = " & s
    Next i

    Cells(7, 1) = "1 から " & N & " までの和"
    Cells(7, 2) = s
End Sub

2.1 トレース ── 頭の中で動かす

元資料 13 枚目のトレースです。変数の値を 1 行ずつ書き出して追うこれを 「トレースする」といい、プログラムを読む基本の技術です。

動かす② 1〜5 の和をトレースする 1 行ずつ

ループが終わったあとの i の値に注意。 元資料 13 枚目のトレースは 「s = 0+1+2+3+4+5 → i = 5+1 = 6 ④ ②で判断」で終わっています。 つまりループを抜けたとき、i は最終値より 1 大きい(6)のです。 条件を判断するために、いったん増やしてから比べるからです。 ループのあとで i を使うと N ではなく N+1 なので、ここでよく間違えます。

2.2 Step を使う

Sub Macro2()
    Dim i As Integer
    Dim r As Integer

    ' 1 から 10 まで、2 つ飛ばし
    r = 1
    For i = 1 To 10 Step 2
        Cells(r, 1) = i
        r = r + 1
    Next i
    Cells(1, 2) = "← Step 2"

    ' 10 から 1 まで、逆向き
    r = 1
    For i = 10 To 1 Step -3
        Cells(r, 4) = i
        r = r + 1
    Next i
    Cells(1, 5) = "← Step -3"

    ' 9 の段(かけ算九九の一部)
    For i = 1 To 9
        Cells(i, 6) = 9 * i
    Next i
End Sub

3. While 〜 Wend 文

元資料 12 枚目の書き方です。条件が真である間、繰り返します

While 条件式
    ブロック
Wend

Sub Macro3()
    Dim i As Integer
    Dim s As Integer
    Dim N As Integer

    N = 5
    s = 0
    i = 1                  ' ① 初期化を自分で書く

    While i <= N           ' ② 条件を自分で書く
        s = s + i          ' ③ 本体
        i = i + 1          ' ④ 増やすのも自分で書く
    Wend

    Cells(1, 1) = "和"
    Cells(1, 2) = s
    Cells(2, 1) = "抜けたときの i"
    Cells(2, 2) = i
End Sub
For と While のどちらを使うか
For 〜 NextWhile 〜 Wend
向いている場合回数が決まっている いつ終わるか分からない
初期化・増加1 行にまとめて書ける自分で書く(3 か所)
書き忘れの危険少ない増やす行を忘れると無限ループ
1〜N の和、表の 10 行を処理互除法、収束するまで計算(第 8 章)

回数が分かっているなら For を使ってください。 While で書くと、初期化・条件・増加が3 か所に散るので、 どれか 1 つを書き忘れて無限ループになります。 上のプログラムで i = i + 1 の行を消して実行してみてください ── 本物の Excel なら固まりますが、この教材では 200 万手で自動的に止まります。 安心して失敗を試せます。

While 〜 Wend は古い書き方です。 元資料はこれを使っていますが、現在の VBA では Do While 〜 Loop が推奨されます(第 8 章)。 理由は Do なら Exit Do で途中脱出でき、 終了条件を後ろにも書けるからです。 While 〜 Wend は互換のために残されているだけなので、 これから書くなら Do While 〜 Loop にしてください。

4. セルを繰り返し使う ── 0 から始めてはいけない

元資料 15・16 枚目の例です。同じ文字列を 5 行ぶん書きます。

元資料 16 枚目のトレースは、こう書かれています。

i=0 Cells(i,1) = "*****" を実行
i=0+1=1 Cells(i,1) = "*****" を実行
…(i=4 まで)

しかし Cells(0, 1) は実行できません。 Excel の行番号・列番号は 1 から始まるので、 0 を指定すると実行時エラー(1004: アプリケーション定義またはオブジェクト定義のエラー)になります。 「0〜4 までの 5 回」という数え方は配列の添字では正しいのですが(第 11 章)、 セルの番号としては使えません

下のプログラムで確かめてください (この教材の処理系も、同じようにエラーで止まります)。

Sub Macro4()
    Dim i As Integer

    ' 元資料のとおり 0 から始めると……
    For i = 0 To 4
        Cells(i, 1) = "*****"
    Next i
End Sub
Sub Macro5()
    Dim i As Integer

    ' 直し方 ①:1 から 5 まで数える
    For i = 1 To 5
        Cells(i, 1) = "*****"
    Next i

    ' 直し方 ②:0 から数えたいなら、書くときに +1 する
    For i = 0 To 4
        Cells(i + 1, 3) = "#####"
    Next i

    Cells(6, 1) = "① 1 To 5"
    Cells(6, 3) = "② 0 To 4 で i+1"
End Sub

「0 から数えるか、1 から数えるか」は、 プログラミングでいちばん間違いの多いところです。 Excel のセル・行・列は 1 から、 VBA の配列は既定で 0 から(第 11 章)、 文字列の Mid1 から(第 3 章)── ものによって違います。 迷ったら「最初の 1 個」と「最後の 1 個」を実際に試すのが確実です。

5. 二重ループ ── 縦と横

ループの中にループを書くと、表全体を扱えます(元資料 17・18 枚目)。 外側が 1 周する間に、内側が全部まわります。

Sub Macro6()
    Dim i As Integer
    Dim j As Integer

    ' i 行目に、* を i 個ならべる
    For i = 1 To 8
        For j = 1 To i
            Cells(i, j) = "*"
            Cells(i, j).Interior.Color = RGB(0, 112, 192)
        Next j
    Next i

    Cells(10, 1) = "内側のループの回数が、外側の i によって変わります"
End Sub

内側の For j = 1 To i に注目してください。 終わりの値が固定ではなく i(外側の変数)になっています。 だから 1 行目は 1 個、2 行目は 2 個…と増えていきます。 二重ループの威力はここにあります ── 内側の回数を外側で変えられるのです。

Sub Macro7()
    Dim i As Integer
    Dim j As Integer

    ' 見出し
    For i = 1 To 9
        Cells(1, i + 1) = i
        Cells(i + 1, 1) = i
        Cells(1, i + 1).Interior.Color = RGB(0, 63, 140)
        Cells(1, i + 1).Font.Color = RGB(255, 255, 255)
        Cells(i + 1, 1).Interior.Color = RGB(0, 63, 140)
        Cells(i + 1, 1).Font.Color = RGB(255, 255, 255)
    Next i

    ' 本体
    For i = 1 To 9
        For j = 1 To 9
            Cells(i + 1, j + 1) = i * j
            ' 平方数(i = j)に色をつける
            If i = j Then Cells(i + 1, j + 1).Interior.Color = RGB(255, 235, 156)
        Next j
    Next i
End Sub

6. 平方根の数表(元資料 21・22 枚目)

Sub Macro8()
    Dim i As Integer

    Cells(1, 1) = "n"
    Cells(1, 2) = "√n"
    Cells(1, 3) = "n の 2 乗"
    Cells(1, 1).Font.Bold = True
    Cells(1, 2).Font.Bold = True
    Cells(1, 3).Font.Bold = True

    For i = 1 To 10
        Cells(i + 1, 1) = i
        Cells(i + 1, 2) = Format(Sqr(i), "#.#####")
        Cells(i + 1, 3) = i ^ 2
    Next i
End Sub

数表を作るのは、繰り返しのいちばん素直な使い方です。i を 1 から 10 まで変えて、同じ計算を i について行い、i 行目に書く」── この形は、あとでグラフを描く(第 15 章)ときにもそのまま使います。 Format(Sqr(i), "#.#####") で桁をそろえているのは、 表として読みやすくするためです(第 3 章の Format)。

7. 標準体重・肥満度・BMI(元資料 27〜31 枚目)

身長と体重を入力し、標準体重、肥満度、BMI(肥満係数)を求めるとともに、 BMI によってメッセージを切り替えよ。

標準体重 (kg) = 身長2 × 22
肥満度 = (体重 − 標準体重) / 標準体重
BMI = 体重 / 身長2

身長の単位は m(メートル)であり、 cm で入力した場合は単位の調整が必要である。

元資料 28 枚目のプログラムは、そのまま動きます。cm で入力するので、 / 10000* 10000 で単位を調整しているところが要点です (1 m = 100 cm なので、2 乗すると 10000 倍の差になります)。

Sub Macro520()
    Dim Height As Single, Weight As Single
    Dim sw As Single, ob As Single, BMI As Single

    Height = Range("A2").Value
    Weight = Range("B2").Value

    sw = Height ^ 2 / 10000 * 22          ' cm を m に直して 2 乗
    ob = (Weight - sw) / sw * 100         ' 肥満度(%)
    BMI = Weight / Height ^ 2 * 10000     ' 体重 ÷ 身長(m)^2

    Range("A4").Value = Format(sw, "#.##")
    Range("B4").Value = Format(ob, "#.##")
    Range("A6").Value = Format(BMI, "#.##")

    If BMI < 18.5 Then
        Range("A8").Value = "低体重"
    ElseIf BMI < 25 Then
        Range("A8").Value = "普通体重"
    ElseIf BMI < 30 Then
        Range("A8").Value = "肥満度1"
    ElseIf BMI < 35 Then
        Range("A8").Value = "肥満度2"
    ElseIf BMI < 40 Then
        Range("A8").Value = "肥満度3"
    Else
        Range("A8").Value = "肥満度4"
    End If
End Sub

この判定の区切りは、日本肥満学会の基準に一致しています (18.5 未満=低体重、18.5〜25=普通体重、25〜30=肥満 1 度、30〜35=2 度、35〜40=3 度、40 以上=4 度)。 ElseIf が上から順に調べるので、 BMI < 25 は「18.5 以上 25 未満」の意味になります(第 6 章 §1)。

ここで繰り返しを使うと。 元資料のプログラムは 1 人分です。クラス 30 人ぶんを一度に処理したいなら、 全体を For で囲んで、Range("A2")Cells(i, 1) に変えるだけです。 「1 人分を書いてから、ループで囲む」── これがプログラムを育てる基本の手順です。

Sub Macro9()
    Dim i As Integer
    Dim Height As Single, Weight As Single
    Dim sw As Single, ob As Single, BMI As Single

    For i = 2 To 6                        ' 2 行目から 6 行目まで
        Height = Cells(i, 1).Value
        Weight = Cells(i, 2).Value

        sw = Height ^ 2 / 10000 * 22
        ob = (Weight - sw) / sw * 100
        BMI = Weight / Height ^ 2 * 10000

        Cells(i, 3) = Format(sw, "#.##")
        Cells(i, 4) = Format(ob, "#.##")
        Cells(i, 5) = Format(BMI, "#.##")

        If BMI < 18.5 Then
            Cells(i, 6) = "低体重"
            Cells(i, 6).Interior.Color = RGB(183, 230, 240)
        ElseIf BMI < 25 Then
            Cells(i, 6) = "普通体重"
            Cells(i, 6).Interior.Color = RGB(198, 239, 206)
        Else
            Cells(i, 6) = "肥満"
            Cells(i, 6).Interior.Color = RGB(255, 235, 156)
        End If
    Next i
End Sub

8. まとめ

  1. 繰り返しの目的は同じことを 1 か所にまとめること。同じ処理が 2 か所にあるのは形が整っていない印
  2. For 制御変数 = 初期値 To 最終値 [Step 増分値]Next。 回数が決まっているならこちら
  3. ループを抜けたとき、制御変数は最終値より 1 大きい
  4. While 〜 Wend は条件による繰り返し。増やす行を忘れると無限ループ。 新しく書くなら Do While 〜 Loop(第 8 章)
  5. Cells(0, 1) はエラー。セルの行・列は 1 から
  6. 二重ループでは内側の回数を外側の変数で変えられる
  7. プログラムは1 件分を書いてから、ループで囲んで育てる

章末問題

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

もとにしたのは「情報学入門(VBA)142-2021」全 31 枚 (および同内容の 140-2019・19 枚)です。

元資料この教材種別
16 枚目のトレースが i=0 で Cells(i,1) を実行すると書いている Excel の行・列は 1 から。Cells(0,1) は実行時エラーになる。 実際にエラーになる例と、直し方 2 通りを載せた誤り
「薬を飲む」の流れ図で「薬を 1 錠飲む」が 2 か所にある(①と③) 答えは合うが繰り返しの形が整っていない。 処理を 1 か所にまとめた流れ図を並べ、なぜそれが大事か(片方だけ直す事故)を示した構成
While 〜 Wend を使っているが、古い書き方であることに触れていない 現在は Do While 〜 Loop が推奨(Exit Do が使える)。互換のために残っている書き方だと明記古い
トレース(13 枚目)が画像で、抜けたときの制御変数が最終値+1 になることを本文で説明していない 1 行ずつたどれるトレース表にし、i が 6 になることを明示不足
For と While の使い分けの指針がない比較表を追加(回数が決まっているか/書き忘れの危険)不足
図形の表示・二重ループがスクリーンショットのみ 実行できる形にし、内側の回数を外側の変数で変えられることを要点として示した体裁
標準体重のプログラムは 1 人分のみ 「1 件分を書いてからループで囲む」手順を示し、5 人ぶんの版を追加不足
資料 140(19 枚)と 142(31 枚)がほぼ同内容で 2 本ある142(2021 年改訂版)を採り、1 つの章にまとめた重複

元資料で正しかったこと。 ForWhile の書式、13 枚目のトレースの数値 (s が 1→3→6→10→15、i が 2→3→4→5→6)、 27 枚目の 3 つの計算式(標準体重=身長2×22、肥満度、 BMI=体重/身長2)、 28 枚目のプログラム(cm→m の単位調整 / 10000* 10000Dim Height As Single, Weight As Single と型を 1 つずつ書いている点、 BMI の 6 段階の区切りが日本肥満学会の基準と一致すること)── いずれも正確です。 28 枚目のプログラムはこのページで実行して確認しました。