VBA編其ノ14 for、for each、select case
ハイ、今回はfor、for each、select caseを扱いたいと思います。 for・for eachはループ処理、select caseは条件分岐の構文で、それぞれループ処理ではdo loopを、条件分岐ではifをすでに扱ったのですが、今回はそれの言い換え版だと思ってください。 「自分はループ処理はdo loopしか使わないよー」って方もいらっしゃるかもしれませんが、いちおう構文くらいは覚えておいた方がよいと思いますので、お付き合いいただければ。 それでは、はじまりはじまり~。
for文
ではまず、for文について。
さっそくですが、下記のマクロを動かしてみましょう。
このマクロを、下↓のような左上ぴっちりから(1列目1行目から)はじまってる表※1で動かすと、新しい列が挿入され、2列目に連番が振られます※2。
では、解説。
このマクロはfor文の練習用で、中身としては2列目に連番を振るだけのものです。
例によって、新しく列を挿入し、ヘッダー行を1行目に作成してるんですけど、ここは特に説明しなくて大丈夫ですよね※3。
大事なのはここから。
forの構文です。
forの構文はこんなふうに↓※4なっており、仕組みはこんな感じです※5。
「for 変数 イコール 始め to 終わり」と覚えてください。
さっきの例文に照らし合わせてみましょう※6。
最初の「for i = 2」で変数iのスタートの値を設定。
「To lastRw」でストップ値を設定します。
英語でも「to California」は「カリフォルニア行き」の意味ですが、「to ~」で目的地を指しますよね。
それと同様です。
こうして、変数iは2はじまりの3、4、5、…と増えていき、lastRw(最終行)のところでストップします。
よく似た、というかほとんど同じ機能をもつdo loop文と、念のため比較してみましょう※7。
for文の方が少しだけ行数が少なくて済みますね。
do loopはループ処理の外(手前)でi=2を設定しないといけないのですが、for文だと変数の始まりも終わりもセットで1行で設定できます。
あと、i=i+1の有無。
ちがいと言えばそれくらいかな。
機能としてはほぼ同じです。
ただ、他のプログラミング言語だと、ループ処理するための命令にはほぼ必ず「For」を使います。
なので、「For」ってことばに慣れておくと、後々他言語を勉強する際によいかもしれません。
for文において変数は基本的に+1ずつ増加します。
扱うのは基本的に整数で、それが+1ずつ増加する、と思ってください。
1段飛ばし(1、3、5、7、9、…)するにはstepを使います。
for i = 1 to 10 step 2 とすると、(通常+1のところ)+2で進行します。
一応例文を載せておきます。
Sub れんしゅう1()
Dim i As Integer
For i = 1 To 10 Step 2
MsgBox i
Next
End Sub
マイナス1もできます。
下のマクロを動かすと、5、4、3、2、1と減っていきます。
Sub れんしゅう2()
Dim i As Integer
For i = 5 To 1 Step -1
MsgBox i
Next
End Sub
上で「使うのは基本的に整数」と言いましたが、stepを応用すれば一応小数も使えます(変数の宣言をdoubleにすることに注意!VBA編其ノ9を参照)。
下記は0.1ずつ増加するマクロです。
Sub れんしゅう3()
Dim i As Double
For i = 0 To 1 Step 0.1
MsgBox i
Next
End Sub
ということで、次行きます。
for each
ハイ。
今度はfor eachです。
for eachは「すべてのセルに色を塗る」とか「すべてのシートを処理する」といった、対象すべてに同じ処理をする際に力を発揮します。
さっそく例文。
このマクロを動かすと、アクティブなブックのすべてのシート名が表示されます※8。
では、解説。
「for each」の「each」は「各々」とか「それぞれ」という意味です。
「in Sheets」の「Sheets」は「すべてのシート」を指します。
「cells」や「rows」で、「すべてのセル」「すべての行」を指すことができるのですが、それと同様です。
なので、Each sh In Sheetsは、「全シートのなかに含まれる、それぞれのsh(shはワークシートを指す変数)に対して」という意味になります※9。
うーん…、かんたんに説明しようと思ったんだけど、ちょっとむずかしいな…。
実はぼくも、for eachはニガテです。
というのもfor eachは、先が見通せてないとなかなかうまく構文が書けないからで、ぼくのように先頭からテキトーにマクロを書きはじめてしまうタイプにはちょっと扱いづらいんです。
ちょっと下↓、見てください※10。
for eachは「変数」と「範囲」(ループ処理させる作業範囲)を指定してやるのですが、「やりたいこと」を順を追って考えてみると、
1.ループ処理させる範囲を指定しなきゃな…
2.ひとつひとつの値を指す変数の名前を決めてやらないと…
3.あ、そうなると、変数の宣言をしなきゃいけないな…
となっていて、おしりから考えていかないといけないんですね※11。
まあ、なので、だいたいぼくがfor each使うと、「ここはfor each使いたいから、範囲を決めて…→おおっと!変数宣言してないじゃん!」ってなことになりがちです。 気を付けたいものです。
しかし、ですが、安心してください。 同じ処理を、for eachを使わないでやることもできます※12。
どちらも処理としては優劣ありません。
同じ結果となります。
ただ、for eachを使うと、「すべてのシートについて処理がしたいんだな」ということが他の人に伝わりやすくなります。
なので、for eachを使うことにもメリットはありますので。
閑話休題。
上記で、for each文をfor each使わないマクロに書き換えました。
for eachをfor文に直しただけで、基本的な作りは変わっていないのですが、そのなかで1か所、最後の行、シート枚数を数えるところに小さなちがいがあることに気付いた方もいらっしゃるかと思います。
なぜ変えたかというと、for文と同じ行で使った変数、つまりfor i = 1 to 5でのiは、最後forループを抜ける際になぜかもう1回+1されてしまい、toで指定した数値で終わらないのです。
例えば、以下のマクロを動かすと、カウンターストップに「5」を指定しているので最後「5」が表示されてほしいのですが、なぜか+1されて「6」となります。
Sub test()
Dim i As Integer
For i = 0 To 5
MsgBox "(ループ処理中)" & i
Next
MsgBox "(ループが終わった後の数値は)" & i
End Sub
おそらく、推測ですが、最後のNextが効いてしまったんだと思います。
i=5となる、Nextでi=6、ただしi=6は5をオーバーするから処理中止、けど処理は中止されたけどi=6になってしまっている…、みたいな。
ちなみに、問題が起こるのは「forと同じ行で使った変数」です。
つまり、for i = 1 to 5の「i」が対象です。
ループの中身に使っている変数(Tabで1字下げにしている部分の変数)については問題ありません。
なので、下記マクロだと、変数jは大丈夫ですが、変数iはおかしくなります。
Sub test2()
Dim i As Integer
Dim j As Integer
j = 0
For i = 1 To 5
j = j + 1
MsgBox i & "回目:j = " & j
Next
MsgBox "i = " & i
MsgBox "j = " & j
End Sub
for each、もいっちょやってみましょう。
下記マクロを動かしてみてください。
こちらのマクロを空のシートで動かすと、cells(1,1)からcells(5,5)までの範囲、5×5=25マスに1から順に連番が入力されます※13。
かんたんに解説。
今回はセル(1,1)からセル(5,5)までを範囲とするので、「Range(cells(1,1),cells(5,5))」と範囲を指定してやります。
そうなると、その範囲を踏破する変数が必要なので、それを今回は「c」という変数名で指定します。
コイツは、セル(1,1)からセル(5,5)までを範囲を、ひとつひとつ巡回してくれます。
さて、セルのひとつひとつは、Rangeオブジェクトです(VBA編其ノ9参照)。
なので、変数cを、「As Range」と宣言してやります※14。
…うーん、やっぱりむずかしかったかな。 for eachは、ふつうのfor文で言い換えることがだいたいにおいて可能なので、無理に覚えなくてもいいんだけど、でも、「すべてのセルを~」とか「すべての行を~」とかの処理をしたいときに読み手としてはわかりやすかったりするので、ま、余裕があったら使えるようになっておくとよいかもしれません。
select caseで場合分け
ハイ、では最後。
セルの値に応じて、1なら黒、2なら白、3なら赤…、という具合に、色を塗り分けるマクロを書く、とします。
まず、if文を使って書いてみましょう。
このマクロを下記のように1列目にNoが振ってあるExcelに対して動かすと、、1列目のNoの値に応じて、色が塗られます※15~16。
ただ、このマクロ、elseifがいっぱい出てきてうっとおしい…。
僕らがやりたいことは、「1ならコレ、2ならコレ、…、」っていう、各分岐が均等の重みをもっているシンプルな場合分けです。
こういうときはselect caseを使った方が、わかりやすくなります。
こいつを動かすと、さっきのと同じ結果なのですが…※17。
結果は同じなのですが、しかしマクロはすっきりします。
ちょっと比較してみましょう※18~20。
Elseifは、どうしても「最初の条件じゃない場合、次の条件」「それもダメだった場合、そのまた次の条件」というふうな思考回路をしてしまうので、経路が煩雑になります。
そしてまた、その構造上Elseif文はどうしても、「上の方に書かれた条件が優先される(逆に言えば、2コ目の条件は1コ目の条件が満たされなかったときはじめて発動する)」という動き方をします。
それに対して、Select Caseは、「セル(1,1)が1のときはこう」「2のときはこう」という、シンプルな分岐です。
だから、やりたいことが単純な場合分けであるなら、Select Caseを使った方が読み手にわかりやすいでしょう。
では、Select Caseの使い方。 Select Caseの使い方は、以下の通りとなります※21。
select case、「場合を選択する」って意味ですね。
select caseはおしりにend selectを用い、その間に処理を書きます。
select caseのうしろには場合分けのスイッチとなるもの(今回はcells(i,1).value)を記入し、それの変化のさまをcase ○○で書いていきます。
練習のため、もういっちょやってみましょう。
テストの点数に応じて優/良/可/不可を入れるマクロです。
コイツを動かすと、こう↓なります※22。
かんたんに解説しておきます※23。
「80点から100点のあいだ」「60点から79点のあいだ」ってのには「○○ To ○○」を使います。 で、「上記条件以外の場合」には「Case Else」を使います。
ちなみに、戯れにif文でも似たような内容のマクロを書けますが…、冗長ですし、ダサイです※24。
ただ、ダサイことはダサイのですが、(上記のとおり)if文でもなんとか動くものは書けます。
初学者にとって扱いやすいのはif文です。
これは、ワークシート関数にif関数があることに起因するのかもしれません。
なので、最初はelseifをいっぱい使ったマクロを書いてしまうと思うのですが(ぼくも最初はそうでした。こればっかりは仕方ありません、誰もが通る道です)、経験を積んで余裕が出てきたら、「…そういえばselect caseって書き方もあったな…」ってこと、思い出してみてください。
記述の幅が広がりますので。
と、いうことで、今回もいろいろやってきました。
今回紹介した構文は、これまでやってきたループ処理や条件分岐でも実現可能なものだったりもするのですが、まあ、余裕があったら覚えておいて損はないと思います。
では、シーユーアゲン!