ループ
メニューを表示するにはスワイプしてください
これまで紹介したテクニックは、すべて1つの値に対してのみ動作していました。ループは、同じロジックを複数の値(5つの商品、500件の注文、テーブルに含まれる任意の行数など)に連続して適用する方法です。同じコードブロックを何度もコピー&ペーストする必要はありません。
For...Next
決まった回数だけコードブロックを繰り返し実行します:
Dim i As Long
For i = 1 To 5
Debug.Print "Row " & i
Next i
' Step lets you skip or count backward
For i = 10 To 1 Step -1
Debug.Print i
Next i
最初のループでは、iは1から始まり、その値で内部のコードが1回実行されます。その後、iは自動的に1ずつ増加し、ブロック全体が再度実行されます。iが5を超えるまで繰り返され、超えた時点でループは停止し、Nextの後に処理が進みます。Stepで増分を変更できます:Step -1はデフォルトの1ずつ増加ではなく、1ずつ減少します。Step 2なら2ずつカウントします。ループ変数iは特別な構文ではなく、VBAが各ループごとに自動で更新する通常のLong型変数です。
For Each
コレクション内のすべての要素を直接ループ処理します。要素数やカウンターの管理は不要です:
Dim cell As Range
For Each cell In ThisWorkbook.Worksheets("Products").Range("B2:B6")
Debug.Print cell.Value
Next cell
ここではcellが範囲B2:B6内の各セルを順番に受け取り、その中身を.Valueで取得します。For...Nextとの違いは、For...Nextでは数値でカウントし、その数値で何かを参照しますが、For Eachではアイテム自体を直接処理するため、ワークシート範囲を扱う際にはより直感的です。
Do While
条件がTrueである限り繰り返します。何回繰り返すかは事前には分かりません。これはFor...Nextとは異なります:
Dim stock As Integer
stock = 134
Do While stock > 0
stock = stock - 30 ' simulate selling in batches of 30
Loop
Debug.Print stock ' whatever's left after the loop stops
VBAは毎回ループの最初にstock > 0をチェックします。手作業で追ってみると:134 → 104 → 74 → 44 → 14、そして再度条件をチェックします。14はまだ0より大きいので、もう一度ループが実行され、-16になります。この時点でstock > 0がFalseとなり、ループは-16で終了します。0や14できれいに止まるわけではありません。初めて使う際は注意深く追跡する価値があります。Do Whileループは、条件がチェックされた時点で成り立つ限り繰り返されるため、ループ内の処理が意図した境界を超えてしまうことがよくあります。
Do Until
Do Untilもあり、これはDo Whileの逆です。条件がFalseの間繰り返し、条件がTrueになった瞬間に停止します。どちらが自然に読めるかは、ビジネスルールをどう表現するかによります。「在庫が残っている間続ける」ならDo While、「在庫がなくなるまで続ける」ならDo Untilです。どちらも同じ動作を実現できますが、表現の方向が異なります。
Exitステートメント
ループの終了条件に達する前に、途中でループを抜けることができます:
For i = 1 To 100
If i = 10 Then Exit For ' stop the loop immediately
Debug.Print i
Next i
このループは本来100までカウントしますが、iが10になった瞬間にExit Forで即座に終了します。Exit Forの後の処理は実行されず、Next iの次の行にジャンプします。リストを検索して目的のものが見つかった時点で処理を止めたい場合などに使います。
総まとめ
この章で学んだすべてのテクニック(変数、演算子、関数、条件分岐、そしてループ)は、以下の手続きで組み合わさっています:
Option Explicit
Sub CheckStockLevels()
' Sample data standing in for the Products table
Dim products(1 To 5) As String
Dim stockLevels(1 To 5) As Integer
Dim reorderLevels(1 To 5) As Integer
Dim i As Long
Dim alertMessage As String
products(1) = "Laptop Stand": stockLevels(1) = 58: reorderLevels(1) = 20
products(2) = "Wireless Mouse": stockLevels(2) = 134: reorderLevels(2) = 30
products(3) = "USB-C Hub": stockLevels(3) = 44: reorderLevels(3) = 15
products(4) = "Monitor Arm": stockLevels(4) = 18: reorderLevels(4) = 10
products(5) = "Keyboard": stockLevels(5) = 61: reorderLevels(5) = 20
alertMessage = ""
For i = 1 To 5
Select Case stockLevels(i)
Case Is <= reorderLevels(i)
alertMessage = alertMessage & products(i) & _
" — REORDER NOW" & vbNewLine
Case Is <= reorderLevels(i) + 10
alertMessage = alertMessage & products(i) & _
" — watch closely" & vbNewLine
End Select
Next i
If alertMessage = "" Then
MsgBox "All products are adequately stocked."
Else
MsgBox "Stock Alerts:" & vbNewLine & alertMessage
End If
End Sub
- Dim products(1 To 5) As Stringとその下の2行は配列の宣言です。各配列は5つのボックスが並んだ行で、products(1)が1つ目の商品名、products(2)が2つ目、というように使います。
- コロン(
:)で区切られた5行は、1行に複数の代入文をまとめて記述しています。products(1) = "Laptop Stand": stockLevels(1) = 58: reorderLevels(1) = 20は、3行に分けて書くのと全く同じ意味です。 For i = 1 To 5 ... Next iは5つの商品を1つずつ処理します。iはループカウンターであり、各配列のインデックスとしても使われます。Select Case stockLevels(i)は、2.4節で紹介した範囲ベースの判定を、今度は実際の配列データに対して実行しています。- Case Is <= reorderLevels(i)は、在庫が再発注レベル以下になった商品を検出します。reorderLevels(i)自体が変数なので、各商品ごとに異なる基準値で判定されます。
alertMessage = alertMessage & ... & vbNewLineは、文字列を1行ずつ追加していきます。vbNewLineは改行を挿入する組み込み定数で、最終メッセージでは各商品が個別の行に表示されます。- 最後の
If alertMessage = "" Thenの判定で、どちらのMsgBoxを表示するかが決まります。空文字列なら、ループでフラグが立つ商品がなかったことを意味します。
これを実行すると、モニターアームが「watch closely」(在庫数18、再注文レベル10)としてフラグ付けされているのが確認できます。なぜ2番目のCaseでのみ検出され、最初のCaseでは検出されないのかを正確に確認したい場合は、i = 4でループを手作業で追跡してください。このプロシージャは、配列、ループ、Select Case、文字列構築を組み合わせて使用しています。これはすでに本番品質に近いロジックであり、実際のワークシートではなく、コード内に直接入力されたサンプルデータで動作しています。
課題
CheckStockLevelsをワークブックにコピーして実行し、メッセージがスクリーンショットと一致することを確認してください。- ワイヤレスマウスの在庫を25に変更して再実行してください。今度は「watch closely」と表示されるはずです。
- その下に
Do Whileループを追加し、ラップトップスタンドの在庫が20以下になるまで一度に15個ずつ販売するシミュレーションを行い、各販売後に即時ウィンドウに累計を出力してください。 - チャレンジ:
For i = 1 To 5のループを、代わりにVariant配列に対してFor Eachを使って書き直してください。ロジックは依然として機能しますか?
2. ワイヤレスマウスの在庫数の変更
- ワイヤレスマウスのデータを設定している行を探します:
products(2) = "Wireless Mouse": stockLevels(2) = 134: reorderLevels(2) = 30。 - 真ん中の数字(
134)だけを25に変更します。商品名と発注点はそのままにします。 - 実行前に、どの
Caseが該当するか手計算で確認します: 25 ≤ 30(発注点)ですか?特にCase Is <= reorderLevels(i)の行と比較してください。2番目の行ではありません。
3. Do Whileによるバッチ販売のシミュレーション
- ノートパソコンスタンドの在庫数を保持する変数が必要です。
stockLevels(1)(58)と同じ値で開始します。これにより、マクロ内の他の値とずれないようにします。 - ループ条件は在庫が20より大きいかどうかを確認します。在庫が20以下になった時点でループを終了します。
- 1回ごとに15を減算し、減算後すぐに新しい値を出力します(減算前ではありません)。これにより、出力される各数値が販売後の在庫数を反映します。
- 紙で追跡してみましょう: 58 → 43 → 28 → 13。最後の値が20を下回るのは、ちょうど20で止まらずにオーバーシュートする
Do Whileの動作によるもので、バグではありません。これは本章のループ図でも説明されています。
ポイント2 — CheckStockLevels内で、1つの数字だけを変更します:
products(2) = "Wireless Mouse": stockLevels(2) = 25: reorderLevels(2) = 30
再度Subを実行します。
ポイント3 — 既存のコードの下に、まだCheckStockLevels内(End Subの前)で次を追加します:
' --- Simulate selling Laptop Stand in batches of 15 ---
Dim laptopStandStock As Integer
laptopStandStock = stockLevels(1)
Do While laptopStandStock > 20
laptopStandStock = laptopStandStock - 15
Debug.Print laptopStandStock
Loop
フィードバックありがとうございます!
AIに質問する
AIに質問する
何でも質問するか、提案された質問の1つを試してチャットを始めてください