VBAで擬似continueを実現する(失敗)

擬似continue

VBAのイマイチなところ選手権」でもやったら、かなり上位に食い込むだろうと思うのが、

Forループでcontinueが使えない

ことだと思う。

breakに相当する命令があるだけに、なおのこと歯痒いことだろう。

テスト用コード

リスト1 標準モジュール
Public Sub testContinue()
  Dim ar As Variant
  ar = Array("アホ", "バカ", "クズ", _
             "カス", "デコスケ", "ロクデナシ", _
             "ウジムシ", "ボケ", "スットコドッコイ", _
             "ち~んw")
  Dim i As Long
  For i = 0 To 9
    Debug.Print ar(i)
  Next
End Sub

素数10の配列を準備して、各要素をDebyg.Printでイミディエイトに書き出すだけのコード。

たとえば、こいつを「3の倍数個目は出力しない」みたいな条件で作動させることを考える。

Forブロック内部をIfブロックにおさめる

continueしたい条件のときは何もしない、ということを、If文で実現する考え方。

リスト2-1 標準モジュール
Public Sub testContinue()
  Dim ar As Variant
  ar = Array("アホ", "バカ", "クズ", _
             "カス", "デコスケ", "ロクデナシ", _
             "ウジムシ", "ボケ", "スットコドッコイ", _
             "ち~んw")
  Dim i As Long
  For i = 0 To 9
    If ((i + 1) Mod 3) = 0 Then
    Else
      Debug.Print ar(i)
    End If
  Next
End Sub

もちろん、これで意図した通りの出力は得られるが、処理の本体がそこそこのサイズの場合、全体をIfブロックの中身にする、というのはあまりにブサイク。

Nextの手前にラベルを置いてGoToでジャンプする

題名の通り。Nextの直前にラベルを置いておいて、continueする代わりにGoToでジャンプするやり方。

リスト2-2 標準モジュール
Public Sub testContinue()
  Dim ar As Variant
  ar = Array("アホ", "バカ", "クズ", _
             "カス", "デコスケ", "ロクデナシ", _
             "ウジムシ", "ボケ", "スットコドッコイ", _
             "ち~んw")
  Dim i As Long
  For i = 0 To 9
    If ((i + 1) Mod 3) = 0 Then GoTo JumpTo
    Debug.Print ar(i)
JumpTo:
  Next
End Sub

For文の入り口で条件を確認して、条件に当てはまっていたらNextの手前まで飛ぶので、これでもまあ、意図通りの出力が得られるだろう。

ただ、ラベルは必ず左端に配置されてしまうので、インデントが狂ってしまう。これまたブサイク。

プロシージャを呼び出してループカウンタをインクリメントする

ちょっとこういうおバカなやり方を実験してみた。continueする条件に当てはまっていたら、ループカウンタをその場でインクリメントしてみるw

スト2-3 標準モジュール
Public Sub testContinue()
  Dim ar As Variant
  ar = Array("アホ", "バカ", "クズ", _
             "カス", "デコスケ", "ロクデナシ", _
             "ウジムシ", "ボケ", "スットコドッコイ", _
             "ち~んw")
  Dim i As Long
  For i = 0 To 9
    If ((i + 1) Mod 3) = 0 Then continue i
    Debug.Print ar(i)
  Next
End Sub

Public Sub continue(ByRef i As Long, _
                    Optional ByVal step_ As Integer = 1)
  i = i + step_
End Sub

ループカウンタを参照渡しでcontinueプロシージャに渡す。受け取ったcontinueプロシージャ側では、ループカウンタをインクリメントするだけ。値渡しだと、「i」は呼び出し側と呼び出され側で別物扱いになるので、珍しく参照渡し。これだとcontinueプロシージャ側での計算結果が呼び出し元の「i」にも反映される。

実行結果

f:id:akashi_keirin:20180326071330j:plain

いちおう、意図通りの出力が得られた。

おわりに

もちろん、3つ目のやり方が重大な問題(笑)をはらんでいる(したがって使いものにならない)ことは重々承知の上ですw

@akashi_keirin on Twitter