和風スパゲティのレシピ

日本語でコーディングするExcelVBA

よく書くコードは汎用関数に - 作り方と使い方

どんなマクロでも使えるような関数のことを、汎用関数と呼びます。

よく書く処理は、汎用関数にして持っておきましょう。
同じコードを何回も書かなくて良くなりますし、何よりコードが読みやすくなります。


「どんなマクロでも」と聞くと、難しそうな印象を受けるかもしれませんが、
「汎用関数は単純な処理ほど効果を発揮する」ため、実際に作るのは実は簡単です。


サンプルコードを見ながら進めていくとイメージをつかみやすいと思いますので、
早速見ていきましょう。

汎用関数のメリット

◇ サンプル:最終行を取得する

ExcelVBAを書いていると必ずついて回るのが最終行の取得です。

最終行 = ws処理シート.Cells(ws処理シート.Rows.Count, 1).End(xlUp).Row
For R = 2 To 最終行

' 変数を用意しないなら
For R = 2 To  ws処理シート.Cells(ws処理シート.Rows.Count, 1).End(xlUp).Row

 
有名なコードですね。
「めっちゃ下のセルからCtrl+↑」を、VBAで書いたコードです。


早速ですが、これを汎用関数にしてみましょう。

Function Get最終行(ws As Worksheet, C As Long) As Long
    Get最終行 = ws.Cells(ws.Rows.Count, C).End(xlUp).Row
End Function

' 使用例
For R = 2 To Get最終行(ws処理シート, 1)

もうそのまんまですね。
まったくひねりのない、ストレートなコードで、関数内はたったの一行です。


しかしこれだけでも、なんとなく良さそうな感じは伝わるのではないでしょうか?

実際にどんなメリットがあるのかを、詳しく解説していきますね。

1.読みやすい

まず1番はこれです。

For R = 2 To ws処理シート.Cells(ws処理シート.Rows.Count, 1).End(xlUp).Row
' ↓
For R = 2 To Get最終行(ws処理シート, 1)

何をやっているコードなのかが、普通に読めるようになります。


今回の最終行取得は、有名かつ頻出なので、
既にぱっと見でわかる方も多いかもしれません。

しかし「.End(xlUp).Row ⇒ 最終行の取得」は、
慣れないうちは解読が必要なコードです。

これが「Get最終行」となることで、処理がストレートに読めるようになります。


関数化(プロシージャ分割)の重要な目的のひとつは、
処理を1行にまとめて名前を付ける」ことです。

処理に名前がついてコードが読みやすくなることは、
汎用関数だけの話でなく、関数そのもののメリットです。


加えて汎用関数は、汎用化するくらいストレートな処理のため、中身を見に行く必要がありません。
読みやすい上に、読み飛ばしやすいというのも大きな強みですね。

2.書きやすい

読みやすさの次は書きやすさですが、細かく分けると↓のようなメリットがあります。

  1. 何度も同じコードを書かなくて済む
  2. プロパティ名、引数名を覚える必要がなく、書き間違いもない
  3. コピペでいい箇所と書き換える必要がある箇所の区別ができる
  4. 関数ならではのテクニックが使える


まず「1.何度も同じコードを書かなくて済む」が素晴らしい点ですね。
今回の最終行はたったの1行ですが、それでも効果があるくらいです。

長いだけで重要じゃないコードを機械的に書く作業を減らせるので、
本当に重要なコード・ロジックに集中できます。


↓こんなのとか、完璧に直訳しただけの関数ですが、超便利ですよ。

Sub オートフィルターをクリアする(指定シート As Worksheet)
    If 指定シート.AutoFilterMode = True Then ' フィルタが設置されていて
        If 指定シート.AutoFilter.FilterMode = True Then ' いずれかの抽出あれば
            指定シート.ShowAllData ' クリアを実行(↑の判定を忘れるとエラーになる)
        End If
    End If
End Function

' メインコードで呼び出すとき
Call オートフィルターをクリアする(ActiveSheet)

こんなただの暗記コードで、メインコードを5行も汚したくないですからね。


次に「2.プロパティ名・引数名を覚える必要がない」ですが、例えば先ほどの最終行の例を見てみましょう。
 

ws処理シート.Cells(ws処理シート.Rows.Count, 1).End(xlUp).Row

これを「見た瞬間に最終行だと思える」ようになるのは、そんな難しくありません。
割とすぐにできるようになります。


しかし、これを「なにも見ずにスラスラ書けるようになる」のは、
意外と難しかったんじゃないでしょうか?

「薔薇は読めるけど書けない」のと同じような話です。


前に使ったコードを探しに行ったり、Googleさんにもう一回聞いたり、
xlupのスペルミスや、.Rowを忘れてバグを書いたりした経験は、
誰にでもあるでしょう。


この暗記作業がなくなって、Get最終行とだけ覚えておけばよくなるということです。


3.コピペ箇所と書き換え箇所の区別」は、その延長の話です。

今回の最終行取得をコピペで持ってきて書き換えるとしましょう。

その時、「.Rows.Count, 1).End(xlUp).Row」部分は読み飛ばしたくなるのですが、
ここは定型文と見せかけて、「1」だけはしっかり考えて書く必要のある値です。


これを関数にしたことで、

Get最終行(ws処理シート, 1)

と、書き換えるべき個所がどこなのかが明確に表現できるということです。


表がB列始まりだったとき、「1」を書き換え忘れてバグを書くことが、極端に減ると思います。


このように、汎用関数を作っておくと、コードを書くのがただ楽になるだけでなく、
バグも少なくなり、サンプルコードを調べる手間も減ります。

コードを暗記レベルに使いこなす訓練をしなくても、
サクサクコードが書けるようになりますので、是非やってみてください。


最後の「4.その他のテクニック」については軽く触れるだけにしておきます。

  • Exit Sub、Exit Functionをその処理専用に使える
  • On Error Resume Nextをその処理専用に使える
  • Optionalを使って似た処理を同じ関数で表現できる

あたりが非常に強力ですので、実際に作るときに検討してみてください。

汎用関数を書く場所と呼び出し方

さて汎用関数を作ったとして、問題は「どこに書いて、どう呼び出すか」です。

作るマクロと同じ場所に書いていては、
もう一度使いたいときに前に使った場所を探す羽目になり、
それでは汎用化した意味がありません。


ということで、「どこか一箇所にまとめて保管し、使いたいときに持ってくる
方法を考えなければいけません。


この方法はいろいろ考えられるのですが、
とりあえず特別な勉強や知識が不要で、かつなかなか便利な方法として、
個人用マクロブックの1つのモジュールにまとめて書いておく
のがおススメです。


「個人用マクロブック」は、Excelを開くと必ず裏で開かれるブックです。

その中に「汎用関数」みたいなモジュールを持っておき、
作る汎用関数はすべてこの1モジュールに書いておきます。(分別はしない)

使うときは「汎用関数」モジュールを使いたいブックにマウスで持ってくるだけというお手軽さ!


個人用マクロブックの作り方自体は簡単で、
マクロの記録を開始し、保存先を「個人用マクロブック」にすれば作成されます。

めんどくさい設定とか一切不要ですし、別PCでも同じ関数を使いたい場合は、
このファイルを送ればいいだけです。
※ どこにあるかはDebug.Print Thisworkbook.Pathで見ることができます。
※ モジュールをエクスポートして送るのもおすすめ


ちなみに、慣れていくとモジュールを分類したくなるかもしれませんが、
汎用関数をモジュール分けするのは最初はやめた方がいいです。

分類を後で変えたくなった時に割と面倒ですし、
使用時にたくさんのモジュールをコピーするのも手間です。

全く使わない関数を大量にコピーしたって特に問題はありません。



なお、この作成するモジュール名は「Ut」など短い名前にしておくと、

プロシージャリスト

このように「モジュール名.」からそのモジュール内のプロシージャを選択呼出することができます。
これも便利ですので、活用を検討してみてください。

ちなみにUtとはユーティリティの略です。

日本語関数名について

せっかくなので、本ブログのメインテーマにも触れておきます。


汎用関数の名前を日本語にすることで、関数化のメリットを、

処理を1行にまとめて名前を付ける
       ⇩
処理を1行にまとめて和訳する

と、パワーアップすることができます。


例として、

データシート

このデータシートから、
みかんだけのデータにしたシートを新しく出力する」としましょう。


私の持っている汎用関数を使うと、

Dim ws出力シート As Worksheet
Set ws出力シート = シートを新規ブックへコピーする(Worksheets("購入歴データ"))

Call 他ブックを参照している数式をすべて値に固定する(ws出力シート)

Call フィルターをクリアする(ws出力シート)
Call フィルターで抽出する("みかん", ws出力シート,"F")
Call フィルター抽出されていないデータを削除する(ws出力シート)

これで済みます。


処理内容に対してコード量が激減しているのは汎用関数の強さなのですが、
加えてノーコメントでも処理内容が文章のように読めるのが日本語関数名の強さです。



関数化の目的を「同じ処理をパーツ化する」と評しているのはよく見かけます。

反面、より重要な目的「処理のまとまりに名前を付けることができる」については、
あまり語られていません。


これが語られない原因を推測しますと、関数化を学んでいる初学者にとって、
英語の名前を付けられることはメリットでも何でもない
からなんじゃないでしょうか?


よく、「なんで関数にするかわからない」と言っている初学者を見かけますが、
それは母国語でない言語でプログラミングを学んでしまっていることの弊害です。

↑のコードを見たら、なんで関数にするのかは説明不要な気がします。


このページに汎用関数を調べに来たということは、

  • 自分一人でマクロを作ることが多い
  • 大規模なシステム構築でなく、特定の業務の効率化が目的

という、模範的VBAerさんが多いのではないかと推測します。


多国籍部隊でチーム開発をすることも多いプログラマの世界と違い、
ExcelVBAは個人開発の多い言語です。


生まれてから死ぬまで外人が見ない、
下手したら生まれてから死ぬまで自分しか見ないマクロなら、
コードが英語でなければいけない理由は、特にありません。


日本語変数・関数も、積極的に活用してみてください。

まとめ

以上で汎用関数の基本解説を終わります。


汎用関数を使うと、読みやすいコードを楽々かけるようになります。

自分の汎用関数が増えていくと、それがコードの辞書みたいになってくれますし、
汎用関数モジュールのコード量が自分の経験値みたいで、たまっていくこと自体がうれしかったりします。


また、汎用関数にするような単純な処理でSubプロシージャ・Functionプロシージャを学ぶことで、プロシージャ分割の練習にもなって、大きなマクロをパーツごとに作る力も身に付きます。

是非トライしてみてください。