読みやすいコードの書き方に関する小ネタです。
ズバリ
Dim i As Long: i = 1
これをやるのはアリなのか?という話です。
VBAでは、「:」を使うことで、複数行のコードを1行に書くことができます。
これをマルチステートメントと呼びます。
これを使うべきか、使わないべきか。
結論から言うと
- 使った行は読みづらくなる。
- 代わり全体の流れが読みやすくなるなら使ってOK
と思っています。
ずらずら理由を書いていきますね。
- マルチステートメントを使った行は見にくくなる
- 処理1つの読みやすさ < 全体の流れの読みやすさ
- 関数にするのがベスト
- おまけ:If文はマルチステートメントにできない
- おまけ:マルチステートメントはエラーで再開できない
マルチステートメントを使った行は見にくくなる
極端な例から行きます。
Dim i As Long: For i = 1 To .Count: Cells(i, 1) = i: Next
↑はA列に連番を入力しています。
イラっと来るレベルで見づらいですね(笑)
分解するとこれ↓
Dim i As Long For i = 1 To .Count Cells(i, 1) = i Next
こんなわかりやすい処理すら難読化するので、
これだけ見ると、あまり使ってはいけない気がします。
マルチステートメントは、使った行を読みにくくする
ことは、基本として認識しておきましょう。
処理1つの読みやすさ < 全体の流れの読みやすさ
マルチステートメントは、使った行を読みにくくします。
その前提でこのコードを見てください。
' オートフィルターのデータエリアをすべて処理 Dim R As Long Dim R1st As Long: R1st = ws処理シート.AutoFilter.Range.Row + 1 Dim RLast As Long: RLast = ws処理シート.AutoFilter.Range.Row + ws処理シート.AutoFilter.Range.Rows.Count - 1 For R = R1st To RLast ~データ1行ごとの処理(Cells(R, ○○), ・・・) ~ Next
どうでしょう?
変数3つの関係と使い道が、わかりやすい気がしませんか?
確かに1行1行の処理(オートフィルターの位置を取得している)は読みづらくなってしまっていますが、「オートフィルターのデータエリアをすべて処理」というコメントがあれば、別に読み飛ばしていい部分です。
プログラムは「その行で何をしているか」よりも、「全体を通してどんな流れの処理をしているか」の方が重要で、そして読み解くのが難しい部分でもあります。
「処理の中身が読みづらくなる代わりに、全体の流れがつかみやすくなる」
ような書き方ができそうだと思えば(+その処理の中身がさして重要でなければ)
マルチステートメントは使ってOKだと思います。
例えば、あのイラっと来る例に挙げた「連番をつける処理」も、
処理A(20行くらい) : データをランキングするための細かい計算と並び替え Dim i As Long: For i = 1 To .Count: Cells(i, 1) = i: Next ' データに連番を付与 処理B(20行くらい):つけたランキングと密接にかかわるデータの加工
これだったら、AとBが近いほど読みやすいかもしれないので、
まあ許せなくもないです。(さすがにやらない方がいいとは思いますけどね)
関数にするのがベスト
ここまではマルチステートメントいいねって書いてきました。
しかし、この「処理を見にくくする代わりに全体を読みやすく」という役割において、
マルチステートメントをはるかに超える力を持ったライバルがいます。
ズバリ「関数=プロシージャ」です。
百聞は一見に如かず。
' オートフィルターのデータエリアをすべて処理 Dim R As Long For R = GetR1stオートフィルター(ws処理シート) To GetRLastオートフィルター(ws処理シート) ~データ1行ごとの処理(Cells(R, ○○), ・・・) ~ Next ' オートフィルターの位置取得関数(汎用関数のモジュールにでも書いておく) Function GetR1stオートフィルター(ws対象シート As Worksheet) As Long GetR1stオートフィルター = ws対象シート.AutoFilter.Range.Row + 1 End Function Function GetRLastオートフィルター(ws対象シート As Worksheet) As Long GetRLastオートフィルター = ws対象シート.AutoFilter.Range.Row + ws対象シート.AutoFilter.Range.Rows.Count - 1 End Function
これはもう関数の圧勝ですね。
関数は、処理を見にくくするどころか、処理が見えなくなります。
その代わりにその部分に名前を使られるので、ものすごく処理の流れが見やすくなります。
しかも、別の場所に使いまわしが効くというおまけつき。
- 意味さえ分かれば、中の処理は重要でない
- なるべくその部分を1行で済ませたい
- 全体の流れに邪魔なくらい何回も出てくる
を解決するのは、まさに関数の真骨頂です。
そこは忘れないようにしておきましょう。
あくまでマルチステートメントは補助的な使い方で。
おまけ:If文はマルチステートメントにできない
実は「If」には:が使えません。
より正確には、:の後に「End If」を書くとエラーになります。
もし使えたとしても「分岐を1行で表現しよう」なんて暴挙には出ないと思うので、
この仕様を知っている意味はないと思いますが。
ちなみにIf文は、Elseなし単行の場合は、「:」がなくても1行で書けます。
If isデータの入力不備がある Then Exit Sub
こちらは便利なので覚えておきましょう。
おまけ:マルチステートメントはエラーで再開できない
マルチステートメントでエラーが発生した場合、
その部分を書き換えようとすると、
このアクションを実行するとプロジェクトがリセットされます。実行しますか? |
になってしまうので、「エラーを直してその場所から再開する」ことができません。
例えば
Dim i As Long: i = "あ"
↑この行で止まったとき、あを1にして、再開することはできません。
その場でトライ&エラーを繰り返して直したくなるような複雑な処理は、
マルチステートメントになんかしないので、こちらも割とどうでもいい知識です。
ちょっとイラっと来たことはありますが、この仕様で困ったことはありません。