【VBA】再帰処理について解説

本ページはプロモーションが含まれています

別のページで再帰処理を使ったプログラムを作ったわけですが、昔再帰処理を一生懸命後輩に教えたことを思い出したので個別に解説ページを作ってみました

利用シーンはあまり多くないものの、知っておくと便利な処理なので是非一読を

再帰処理とは

一言で表すと関数やプロシージャが自分自身を呼び出す処理のこと

処理方法の話なので当然VBAだけでなく他のプログラム言語でも使われる処理ですが、私が一番しっくりきたパソコンのフォルダ潜りながらフォルダ情報を収集するプログラムを例に解説します

サンプルプログラム

メチャメチャシンプルなサンプルプログラムです

何をやっているかと言うとファイルが保存されているフォルダを起点にサブフォルダを照会して、今度はそのフォルダを起点にしてサブフォルダを照会して・・・を繰り返します

Sub AddFolderList()
'------------------------------
' メインプロシージャ
'------------------------------
Dim objFso As New Scripting.FileSystemObject

Debug.Print "RootFolder:" & ThisWorkbook.Path
Call SearchFolder(objFso.GetFolder(ThisWorkbook.Path), 1)
End Sub
'------------------------------------------------------------------------------------------
Function SearchFolder(TargetFolder As Scripting.Folder, FolderLv As Long)
'------------------------------
' 再帰処理用プロシージャ
'------------------------------
Dim objFld As Scripting.Folder

Call GetFolderInfo(TargetFolder, FolderLv)
For Each objFld In TargetFolder.SubFolders
    Call SearchFolder(objFld, FolderLv + 1)
Next
End Function
'------------------------------------------------------------------------------------------
Function GetFolderInfo(TargetFolder As Scripting.Folder, FolderLv As Long)
'------------------------------
' フォルダ情報をイミディエイトに表示
'------------------------------
Debug.Print String(FolderLv, " ") & "FolderName:" & TargetFolder.Name
Debug.Print String(FolderLv + 2, " ") & "├FileCount:" & TargetFolder.Files.Count
Debug.Print String(FolderLv + 2, " ") & "├TotalSize:" & TargetFolder.Files.Count
Debug.Print String(FolderLv + 2, " ") & "├CreateDate:" & TargetFolder.DateCreated
Debug.Print String(FolderLv + 2, " ") & "└UpdateDate:" & TargetFolder.DateLastModified
End Function

再帰処理のイメージ

まず再帰処理を使わずに再現しようとした時のプログラムがこちら

フォルダ1-Aを起点とするとその中にフォルダ2-A/2-Bがある、2-Aの中にフォルダ3-Aがあってさらにその中にフォルダ4-A/4-B/4-Cがある

事前に構造がわかっていれば↓の別サンプルのようにFor Nextを必要回数入れ子にして対応することもできますが例えば4-Aの中に新しいフォルダが作成された時、階層がもう一段階深くなるので入れ子が足りず取得できません

無理矢理運用するなら10回くらい入れ子にしておけば大体大丈夫ですがそれではイマイチ

Function SearchFolder(main As Scripting.Folder)
'------------------------------
' 再帰処理しない時のプログラム例
'------------------------------
Dim sub1 As Scripting.Folder, sub2 As Scripting.Folder, sub3 As Scripting.Folder

Call GetFolderInfo(main, 1)
For Each sub1 In TargetFolder.SubFolders
    Call SearchFolder(sub1, 2)
    For Each sub2 In sub1.SubFolders
        Call SearchFolder(sub2, 3)
        For Each sub3 In sub2.SubFolders
            Call SearchFolder(sub3, 4)
        Next
    Next
Next
End Function

そしてこちらが再帰処理のイメージ

Mainフォルダに1-Aを代入すると2-A/2-BがSubフォルダとして取得できる

Subフォルダ2-AをMainフォルダに代入すると次は3-AがSubフォルダに、続いて3-AをMainフォルダにすると4-A/4-B/4-CがSubフォルダになる

ポイントはここでさらに下の階層があればループが続くし、なければここで処理が終わるところ

プログラムも短くスッキリした感じになります

再帰処理のメリット・デメリット

結局のところ再帰処理って必要な処理なの?メリット・デメリットは?ってことでまとめてみます

メリット

繰り返すべき回数が可変なプログラムでも対応することができます

今回のフォルダであれば起点から3階層潜ったわけですが当然もっと深い階層がある可能性もありますがサブフォルダがあればループする仕様になるのでプログラムとして最適化できます

デメリット

再帰処理は処理の途中で別のことに手を付けるようなことなのでループする度メモリを使うことになり、やり過ぎはメモリ不足を招く可能性があります

もう一点はデメリットと言うより注意点になりますが、処理方法については無限ループになってしまいます

例えばこんな処理

'==============================
' 無限ループになるプログラム
'==============================
Sub sample()
Dim i As Long

i = 1
Call AdditionFunc(i)
End Sub
'-------------------------------
Function AdditionFunc(i As Long)
Debug.Print i
Call AdditionFunc(i + 1)
End Function

加算し続けるだけで終わりのない無限処理になってしまうのでループに条件を付けるもしくはループを抜ける分岐を付けてあげる必要があります

'==============================
' 無限ループを回避したプログラム
'==============================
Sub sample()
Dim i As Long

i = 1
Call AdditionFunc(i)
End Sub
'------------------------------
Function AdditionFunc(i As Long)
Debug.Print i
'---10未満の時だけ再帰処理する
If i < 10 Then
    AdditionFunc = AdditionFunc(i + 1)
End If
End Function

あとがき

再帰処理をフォルダ検索を例に解説してみました

わかるようでわからない再帰処理も実例があるとしっくりきませんか?私はきましたw

他の言語でも考え方や書き方は同じなので参考にしてみてください

コメント

タイトルとURLをコピーしました