構造体についての考察

こんにちは、Mottyです。
久々の投稿となりまして、今回はExcelVBAの「構造体」の機能について記述しました。
##概要
構造体とは、複数の変数を１つのグループにまとめたものです。
構造体を使うことで新しく何かができる、ということはないのですが、
変数を１つのグループにまとめて取り扱うことによって
似たような処理をまとめて実行しやすくなったり、コード自体が読みやすくなったりします。


![ 2020-06-15 14.03.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302420/68de5974-1108-52a8-36a0-41b706ff48c0.png)

##VBAでの記述

VBAで記述することができます。
例として、個人情報の名前にアクセスするコードを記述しました。
構造体を使わずに記述する場合、変数に直接アクセスすることになります。

<構造体なし>

```vb:Main.bas

Sub Main() ' 構造体なし

Dim Name As String
Dim ID As String
Dim Adress As String
Dim PhoneNumber As String

Name = "Tanaka"
ID = "001"
Adress = "Tokyo"
PhoneNumber = "090-XXXX-XXXX"

Debug.Print ("名前は" & Name & " です")
' →名前はTanakaです

End Sub

```

<構造体あり>

これに対して構造体を使用する場合は、UserInfoという塊をまず定義してあげます。
そのあと各変数に値を格納します。
デバッグの際は構造体の"名前"プロパティにアクセスする形になります。

```vb:Main2.bas

Public Type UserInfo '構造体定義
    Name As String
    ID As String
    Adress As String
    PhoneNumber As String
End Type

Sub Main2() 
Dim User As UserInfo
    User.Name = "Tanaka"
    User.ID = "001"
    User.Adress = "Tokyo"
    User.PhoneNumber = "090-XXXX-XXXX"

Debug.Print ("名前は" & User.Name & " です")
' →名前はTanakaです

End Sub
```

これだけでは正直違いがわかりませんね・・・。
むしろ構造体を使う方がコード行数が多くなり、面倒なようにも思えます。

結論からいうと、扱う変数の数が多くなれば構造体は役に立ちます。
関連性のあるもの同士を１つのデータ型に格納し、処理等を行うことで
コードがスッキリしますし、情報にもアクセスしやすくなります。

##データ処理

複数の生徒のテスト得点に関する情報がExcel上にあり、
任意の科目の平均点をデバッグするコードを記述してみます。

![ 2020-06-15 14.32.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302420/8e009076-5f62-2a23-cb31-e0c7b34219ba.png)

＜構造体なし＞

```vb:Main.bas
Dim first_row As Long
Dim last_row As Long
Dim name_list() As String
Dim ID_list() As String
Dim English_score() As Long
Dim Math_score() As Long
Dim Japanese_score() As Long
Dim Science_score() As Long
Dim Society_score() As Long

Sub Main()

     Call Initialize  ' 初期化
     Debug.Print (Average_("Science"))
    
End Sub

Sub Initialize() ' 初期化

    ' SCAN行数
     first_row = 2 'Because of Header Exists
     last_row = Cells(Rows.Count, 1).End(xlUp).Row  ' last of "A" Column

    ' 変数再定義
    ReDim name_list(first_row To last_row)
    ReDim ID_list(first_row To last_row)
    ReDim English_score(first_row To last_row)
    ReDim Math_score(first_row To last_row)
    ReDim Japanese_score(first_row To last_row)
    ReDim Science_score(first_row To last_row)
    ReDim Society_score(first_row To last_row)
    
    'SCAN
    For Row = first_row To last_row
          name_list(Row) = Cells(Row, 1)
          ID_list(Row) = Cells(Row, 2)
          English_score(Row) = Cells(Row, 3)
          Math_score(Row) = Cells(Row, 4)
          Japanese_score(Row) = Cells(Row, 5)
          Science_score(Row) = Cells(Row, 6)
          Society_score(Row) = Cells(Row, 7)
    Next
    
End Sub
    

Function Average_(ByVal Subject As String) As Double
    
    first_row = 2 'Because of Header Exists
    last_row = Cells(Rows.Count, 1).End(xlUp).Row  ' last of "A" Column
    
    NUM = last_row - first_row + 1
    sum_score = 0
    
    Select Case Subject
    
        Case "English":
                For Row = first_row To last_row
                    sum_score = sum_score + English_score(Row)
               Next
        
       Case "Math":
               For Row = first_row To last_row
                    sum_score = sum_score + Math_score(Row)
               Next
               
       Case "Japanese":
               For Row = first_row To last_row
                    sum_score = sum_score + Japanese_score(Row)
               Next
               
        Case "Society":
               For Row = first_row To last_row
                    sum_score = sum_score + Society_score(Row)
               Next
               
        Case "Science"
                For Row = first_row To last_row
                    sum_score = sum_score + Science_score(Row)
               Next
            
        Case Else:
            Average_ = -1
            
        End Select
            Average_ = sum_score / NUM

End Function


```

<構造体あり>

各行に存在するデータを構造体（Grade)にまとめて記述していきます。


```vb:Main2.bas

 Public Type Grade
    Name As String
    ID As String
    English As Long
    Math As Long
    Japanese As Long
    Science As Long
    Society As Long
 End Type
 
 Dim first_row As Long
 Dim last_row As Long
 Dim Grade_Score() As Grade

 Sub Main()
 
 Call Initialize
 Debug.Print (Average_("English"))

 End Sub
 
 Sub Initialize()
 

    'SCAN行数
    first_row = 2
    last_row = Cells(Rows.Count, 1).End(xlUp).Row

    '変数再定義
     ReDim Grade_Score(first_row To last_row)
     
     'SCAN
     For Row = first_row To last_row
        With Grade_Score(Row)
             .Name = Cells(Row, 1)
             .ID = Cells(Row, 2)
             .English = Cells(Row, 3)
             .Math = Cells(Row, 4)
             .Japanese = Cells(Row, 5)
             .Society = Cells(Row, 6)
             .Science = Cells(Row, 7)
        End With
    Next
    
End Sub

Function Average_(ByVal Subject As String) As Double
    
    first_row = 2 'Because of Header Exists
    last_row = Cells(Rows.Count, 1).End(xlUp).Row  ' last of "A" Column
    
    NUM = last_row - first_row + 1
    sum_score = 0
    
    Select Case Subject
    
        Case "English":
                For Row = first_row To last_row
                    sum_score = sum_score + Grade_Score(Row).English
               Next
        
       Case "Math":
               For Row = first_row To last_row
                    sum_score = sum_score + Grade_Score(Row).Math
               Next
               
       Case "Japanese":
               For Row = first_row To last_row
                    sum_score = sum_score + Grade_Score(Row).Japanese
                    
               Next
               
        Case "Society":
               For Row = first_row To last_row
                    sum_score = sum_score + Grade_Score(Row).Society
               Next
               
        Case "Science"
                For Row = first_row To last_row
                    sum_score = sum_score + Grade_Score(Row).Science
               Next
            
        Case Else:
            Average_ = -1
            
        End Select
            Average_ = sum_score / NUM

End Function
```

構造体を使うことにより変数同士の関連性がわかりやすいですね。
プロパティへのアクセスも階層的に行えるし、ReDimも一括で行えます。
（いつも思うのですが、VBAの特性上ReDimしなければならない仕様は面倒だと感じます・・・。）

##結論
小規模のものであれば構造体のありなしでほとんど変わりませんが、
作成するプログラムが大規模であればあるほど、構造体は有効になってきます。
（PCの処理に例えるなら、扱うファイルが数十個〜数百個と多くなると
きっちりフォルダを作って階層的にした方がスッキリしますが、
ファイルが高々数個の場合はそのような処理はほとんど影響しない、といった
感じでしょうか。。）

他にもメリット等あれば、教えていただけるとありがたいです。