t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA クラスモジュールを気軽に使う その3

今取り組み中の以下の書籍で、油分け算というパズルが登場する。

Excel VBAでパズルを解こう

Excel VBAでパズルを解こう

3リットル、5リットル、8リットルの容器があり、容器には目盛りがついていない。
8リットルの容器には満タンに酒が入っており、3リットルと5リットルの容器は空だ。
この状態から、5リットル容器に4リットル、8リットる容器に4リットルずつに分けたい。

【条件】目分量は禁止。つまり、一度注ぎ始めたら注ぎ元が空になるか注ぎ先が満タンになるまでストップできない。


と、ここまで説明しておいてなんだが、今回はパズルの解き方の話ではない。
試しにクラスモジュールを使って容器から容器へ注ぐ動作をエミュレートできるようにしてみたところ、メインコードが非常に人間の感覚に近いものになったので紹介したい。

まずCupという名前のクラスを用意し、以下のコードを張り付けておく。

Public 最大容量 As Integer
Dim L As Integer

Property Let 内容量(x As Integer)
    L = x
End Property

Property Get 内容量() As Integer
    内容量 = L
End Property

Sub 注ぐ(注ぎ先 As Cup)
    If 注ぎ先 Is Me Then Exit Sub
    Do While 注ぎ先.内容量 < 注ぎ先.最大容量 And Me.内容量 > 0
        Me.内容量 = Me.内容量 - 1
        注ぎ先.内容量 = 注ぎ先.内容量 + 1
    Loop
End Sub

そして標準モジュールに書くコードはこちら。

Sub 手動油分け算()
    Dim L3 As New Cup: L3.最大容量 = 3
    Dim L5 As New Cup: L5.最大容量 = 5
    Dim L8 As New Cup: L8.最大容量 = 8
    L8.内容量 = 8
    
    L8.注ぐ L3
    L3.注ぐ L5
    L8.注ぐ L3
    L3.注ぐ L5
    L5.注ぐ L8
    L3.注ぐ L5
    L8.注ぐ L3
    L3.注ぐ L5
    
    Debug.Print "L8 "; L8.内容量
    Debug.Print "L5 "; L5.内容量
    Debug.Print "L3 "; L3.内容量
End Sub

…どうだろうか。
L3、L5、L8はそれぞれ、Cup型オブジェクトだ。
そして、L8.注ぐ L3という書き方は、L8からL3に注ぐという命令である。

条件である「一度注ぎ始めたら注ぎ元が空になるか注ぎ先が満タンになるまでストップできない。」という部分は、Cupクラスの内部でやってくれるので使う方は難しいことを考えなくて良い。
我ながら非常にスマートにまとまったと思う。
(もちろん、すごいのは私ではなくオブジェクト指向を生み出した先人達である。)

普段からJavaや.NETなどでオブジェクト指向に親しんでいる人からすると何をいまさらといった話かもしれないが、VBAでクラスモジュールを使ったサンプルは極めて少ないので、今後も使う機会があれば積極的に紹介していこうと思う。

追記

自分に注ぐと無限ループになることに気づき、Cupクラスのコードに以下を追加した。

If 注ぎ先 Is Me Then Exit Sub

このように、修正が局所的になるのもオブジェクト指向の良いところである。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。