11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

1. はじめに

ARアドバンストテクノロジ株式会社(ARI)の鈴木タクヤです。

前回 から少し時間が空きましたが、育児と仕事と法事と 資格の勉強 で忙しかったためです。・・・遊んだりもしたけれど。

んなこたぁ置いといて。

今回はExcelフィルタリングツールのソースコード解説第3回です。

VBA初心者だった私が "今の開発組織でトライした"(←この記事の参加イベント)、業務効率向上ツール作成。せっかく作ったツールを広めるべく、またVBA初心者は実際に動くモノがあった方が勉強しやすいと考え始めた、ツールの導入方法の紹介と、解説形式でのVBA学習用記事シリーズです。(記事執筆も トライ 中。)

(ツールの詳細については以下の記事を、「Excelフィルタリングツール解説シリーズ」タグの記事一覧については以下の記事下部「5. 解説記事のリンク」をご参照ください。)

解説第2回 はSubプロシージャについての記事でしたが、第3回のテーマはそれと似て非なる「Functionプロシージャ」です。

2. Functionプロシージャとは

Functionプロシージャとは、「Function」と「End Function」で挟まれた部分のことです。Subプロシージャと同様に、この中にマクロの処理内容を記述します。

Excelフィルタリングツールにおいては、
isOpened(ByVal bookName As String) As Boolean
setFile(ByVal fullPath As String) As Workbook
checkFormat(ByVal cellReference As String) As String
という3つのFunctionプロシージャが存在します。Subプロシージャと同様に、括弧の前までがFunctionプロシージャ名で、括弧の中はFunctionプロシージャを呼び出す際の 引数(ひきすう) です。また括弧の右のAsの後ろは 戻り値データ型 です。戻り値の設定は、Functionプロシージャ内で 「Functionプロシージャ名 = 戻り値」 のように記述します。

Functionプロシージャの書き方
Function functionName(引数) As 戻り値のデータ型
    ~何かしらの処理~
    (戻り値はプロシージャ内で「functionName = 戻り値」の形式で記述)
End Function

上のFunctionプロシージャを別のFunctionプロシージャやSubプロシージャから呼び出すときは、以下のように記述できます。(Subプロシージャの呼び出し方と同様)

Functionプロシージャの呼び出し方
Call functionName(引数)

Call は省略可能ですが、その場合は引数を括弧で囲みません。また、実際にはFunctionプロシージャの処理結果である戻り値を使用することが多く、その場合は Call を使用せず、以下の例のようになります。

Excelフィルタリングツールより抜粋
'既に対象ブックを開いているか確認する
    If isOpened(targetBookName) = True Then
        Set targetBook = Workbooks(targetBookName)
    Else
        '対象ブックの存在確認&設定
        Set targetBook = setFile(fullPath)
    End If

・・・どれだよ?!と思われたかと思いますが、isOpened(targetBookName) setFile(fullPath) の部分です(処理内容は後述)。Workbooks(targetBookName)も似ていますが、これはWorkbooksプロパティと呼ばれるもので、ややこしいので一旦スルーでお願いします。括弧内で指定した名前のExcelワークブックを表すものだと思っておけばOKです。

引数
プログラムの中で処理を呼び出す際に渡す値のこと。

戻り値
プログラムの中で関数等の処理結果として呼び出し元に返す値のこと。

データ型
変数に入れるデータの種類。一覧については以下のページを参照。初心者はとりあえず、 整数は「Long」、小数は「Double」、文字は「String」、真偽値(○か×か)は「Boolean」 あたりから触れていけば良いかと。VBAにおいてはVariant型というなんでも入る型があるのであまり意識せずとも動くプログラムが書けます。

Subプロシージャとの大きな違いは、 「Functionプロシージャは戻り値を返す」 ということです。「あれやっておいて~」と処理を呼んで、やりっぱなしなのがSub、結果を返してくれるのがFunctionというイメージです。
Functionプロシージャを使いこなせれば、好きな関数を作成してすることができて非常に便利です。

プログラミング言語によっては戻り値のない一連の処理を「プロシージャ」、戻り値のある一連の処理を「関数(ファンクション)」と呼び分けるものもありますが、VBAにおいてはどちらも「プロシージャ」と呼ぶのが一般的なようです。

参考:「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典【関数(function)】

3. ExcelフィルタリングツールにおけるFunctionプロシージャの役割

3-1 isOpened

isOpened(ByVal bookName As String) As Boolean
ブック(Excelファイル)の名前を表すString型の文字列を渡すと、そのブックが開かれている(True)か否(False)かをBoolean型で返します。
(汎用的かと思うので、使えそうならコピペしてください。)

Excelフィルタリングツールより抜粋:流し見してください
'■指定のブックを開いているか否かを判定する関数
Function isOpened(ByVal bookName As String) As Boolean
    isOpened = False
    Dim wb As Workbook
    isOpened = False       '👈戻り値にFalse(偽)を設定
    
    For Each wb In Workbooks
        If wb.Name = bookName Then
            isOpened = True   '👈引数で指定したブックが開かれていたら戻り値にTrue(真)を設定
            Exit For
        End If
    Next
    
    Set wb = Nothing
End Function

3-2 setFile

setFile(ByVal fullPath As String) As Workbook
ブックのフルパス(String型)を引数として渡すと、そのファイルが存在するか確認します。有ればワークブックを開いてWorkbook型の戻り値を返し、無ければメッセージボックスで"ファイル「(引数で渡したフルパス)」が存在しません。"と表示し、 第2回 で紹介したSubプロシージャ「endMacro」を呼び出します。

Excelフィルタリングツールより抜粋:流し見してください
'■対象ファイル存在確認/設定関数(渡されたfullPathのファイルが存在すればそれを返し、存在しなければエラーメッセージを表示する。)
Function setFile(ByVal fullPath As String) As Workbook
    Dim fileChecker As Object
    Set fileChecker = CreateObject("Scripting.FileSystemObject")
    
    If fileChecker.FileExists(fullPath) Then
        Set setFile = Workbooks.Open(fullPath)  '👈戻り値のオブジェクトを設定
    Else
        MsgBox "ファイル「" & fullPath & "」が存在しません。"
        Call endMacro
    End If
    
    Set fileChecker = Nothing
End Function

3-3 checkFormat

checkFormat(ByVal cellReference As String) As String
「A1」「AB28」といったセル番地(String型)を引数として渡します。その文字列がセル番地として有効か否かを判定し、OKであればそれを半角大文字に変換した文字列(String型)を戻り値として返します。NGであれば 第2回 で紹介したSubプロシージャ「showFormatErrorMessage」を呼び出します。
(入力されたセル番地のエラーをハンドリングをするにしても、もっと上手いやり方がありそうですが、半分遊びで作成しました。)

Excelフィルタリングツールより抜粋:流し見してください(長い)
'■セル番地のフォーマットチェック関数
Function checkFormat(ByVal cellReference As String) As String
    '全て半角大文字に置換
    cellReference = UCase(StrConv(cellReference, vbNarrow))
    '文字数を変数に代入
    Dim wordCount As Integer
    wordCount = Len(cellReference)
    
    '文字列が「英字部分・数字部分」の構成になっていることを確認
    If Not cellReference Like "[A-Z]*" Then             '先頭が英字であることを確認
        Call showFormatErrorMessage(1)
    End If
    
    If Not cellReference Like "*[0-9]" Then             '末尾が数字であることを確認
        Call showFormatErrorMessage(wordCount)
    End If
    
    Dim startingPositionOfRowNo As Integer              '数字の開始位置が先頭から何文字目かを格納する変数を定義
    Dim i As Integer, j As Integer                      'ループ処理用変数を定義
    '2文字目以降は1文字ずつ英字か数字か確認
    For i = 2 To wordCount
        If Mid(cellReference, i, 1) Like "[0-9]" Then
            startingPositionOfRowNo = i                 '数字部分(行番号)開始が先頭から何文字目かを保持
            Exit For
        ElseIf Not Mid(cellReference, i, 1) Like "[A-Z]" Then   '数字じゃなくて英字でもなければエラー
            Call showFormatErrorMessage(i)
        End If
    Next i
    
    '数字部分(行番号)の2文字目以降が(存在すれば)全て数字であることを確認
    j = startingPositionOfRowNo + 1
    Do While j <= wordCount
        If Not Mid(cellReference, j, 1) Like "[0-9]" Then       '数字以外だったらエラー
            Call showFormatErrorMessage(j)
        End If
        
        j = j + 1
    Loop
    
    '英字部分(列番号)が上限(XFD)以下であることを確認する
    Dim columnNo As String
    columnNo = Left(cellReference, startingPositionOfRowNo - 1)
    
    '英字部分が4桁以上の場合はエラー
    If startingPositionOfRowNo >= 5 Then
        Call showFormatErrorMessage(4)
    End If
    
    '英字部分が3桁の場合の整合性チェック
    If startingPositionOfRowNo = 4 Then
        If (Mid(cellReference, 1, 1) Like "[Y-Z]") _
            Or (Mid(cellReference, 1, 1) = "X" And Mid(cellReference, 2, 1) Like "[G-Z]") _
            Or (Mid(cellReference, 1, 1) = "X" And Mid(cellReference, 2, 1) = "F" And Mid(cellReference, 3, 1) Like "[E-Z]") _
            Then
            
            Call showFormatErrorMessage(3)
        End If
    End If
    
    '数字部分(行番号)が上限(1048576)以下であることを確認する
    Dim rowNo As Long
    rowNo = CLng(Right(cellReference, wordCount - startingPositionOfRowNo + 1))
    
    If rowNo > 1048576 Then
        Call showFormatErrorMessage(wordCount)
    End If
    
    checkFormat = cellReference        '👈確認済みの文字列を戻り値に設定
End Function

4. 引数の渡し方について

ここまで、引数については「ByVal 引数名 As 引数のデータ型」という書き方をしてきましたが、ByVal ではなく ByRef を指定することもできます。これらは引数の渡し方を意味し、 ByVal は「値渡し」、 ByRef は「参照渡し」です。
ByVal の場合は 元のデータのコピー を引数として渡すので、渡された側の処理でデータが変更されたとしても渡した側のデータは変更されません。
一方、ByRef の場合は 元のデータ自体 を引数として渡すので、渡された側でデータが変更されると、渡した側のデータも変更されます。

参考:「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典【「ByVal」と「ByRef」の違い】

Function checkFormat(ByVal cellReference As String) As String
の戻り値は、値渡しの引数を半角大文字に変換したものなので、
Sub checkFormat(ByRef cellReference As String)
と参照渡しをしても良いということです。
(但し、呼び出し元の記述は少し変える必要があります。)

5. おわりに

以上、Functionプロシージャの解説でした。
「5分で」と謳っておきながら結構ボリューミー(死語?)になってしまいました・・・。FunctionはSubと異なりソースコードに必須ではないですが、使えると便利です。

次回は、今回登場した引数・戻り値とも関連のある 変数 について解説します。次回の更新も期間があいてしまったらすみません。気長にお待ちください。

👇次回

6. 参考文献

(今回は解説中にも参考リンクを挟み込んでいますが、それ以外で。)
こちらはMicrosoftの公式ドキュメントです。

こちらは私がものすごくお世話になっている「エクセルの神髄」という解説サイト。より深く学習を進めたい方は是非。

11
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?