配列もCollectionも使わないデータ構造(Stack)

配列もCollectionも使わないデータ構造

久しぶりに

f:id:akashi_keirin:20191109220848j:plain

VBA Developer's Handbook』を読んだ。

「Chapter 6」の「Creating Dynamic Data Structures Using Class Modules」のところに、実に面白いことが書いてあったので、やってみた。

Stackを二つのクラスモジュールで実現する

データの集合を取り扱う、となると、素人考えでは、配列を使う方法か、Collectionを使う方法しか思いつかない。

しかし、全然違うアプローチが説明されていたので、つい夢中で読んでしまった。英語苦手なくせに。

とりあえず、String型のデータを出し入れするStackを二つのクラスモジュールで実現する。

まず、クラスモジュールを二つ用意し、一方にはStringStack、もう一方にStackStringと名前をつける。

StringStackがStack全体、StackStringがStackに積まれている一つ一つの要素を表す。

クラスモジュール StringStack
Option Explicit

Private topItem As StackString

Public Property Get Top() As String
  If ItemExists Then
    Top = topItem.Value
  Else
    Top = ""
  End If
End Property

Public Property Get ItemExists() As Boolean
  ItemExists = Not (topItem Is Nothing)
End Property

Public Sub Push(ByVal argValue As String)
  Dim newTopItem As New StackString
  newTopItem.Value = argValue
  Set newTopItem.NextItem = topItem
  Set topItem = newTopItem
End Sub

Public Function Pop() As String
  Dim ret As Variant
  If Me.ItemExists Then
    ret = topItem.Value
    Set topItem = topItem.NextItem
  End If
  Pop = ret
End Function

まず、こちらはStack全体を表すStringStackクラス。

プロパティが二つ。

Topプロパティは、Stackのてっぺんに積んであるアイテムの値。今回はString型に限定している。

ItemExistsプロパティは、Stackにアイテムがあるかどうかを表す。

で、メソッドが二つ。

Pushメソッドは、新しいアイテムをStackのてっぺんに積む。

Popメソッドは、てっぺんにあるアイテムを除去するとともに、その値を取得する。

クラスモジュール StackString
Option Explicit

Private value_ As String
Private nextItem_ As StackString

Public Property Let Value(ByVal argValue As String)
  value_ = argValue
End Property
Public Property Get Value() As String
  Value = value_
End Property

Public Property Set NextItem(ByVal argItem As StackString)
  Set nextItem_ = argItem
End Property
Public Property Get NextItem() As StackString
  Set NextItem = nextItem_
End Property

こちらは、Stackに積まれているそれぞれのアイテムを表現するクラス。

Read/Writeのプロパティが二つ。

Valueプロパティは、アイテムの値。今回はString型に限定している。

NextItemプロパティは、自分自身の一つ下に積まれているアイテムを表す。

使ってみる

標準モジュールに次のようなコードを書いて、StringStackクラスを使ってみる。

リスト1 標準モジュール
Private Sub testStringStack()
  Dim strStack As New StringStack '……(1)'
  With strStack
    Call .Push("1番サード岩鬼")    '……(2)'
    Debug.Print .Top              '……(3)'
    Call .Push("2番セカンド殿馬")
    Debug.Print .Top
    Call .Push("3番レフト微笑")
    Debug.Print .Top
    Call .Push("4番キャッチャー山田")
    Debug.Print .Top
   Debug.Print "===================="  '……(4)'
    Do While .ItemExists          '……(5)'
      Debug.Print .Pop
    Loop
  End With
End Sub

まず、(1)の

Dim strStack As New StringStack

で、StringStackクラスのインスタンスを用意する。

その後、(2)の

With strStack
  Call .Push("1番サード岩鬼")
End With

で、Pushメソッドを用いて、「1番サード岩鬼」というデータ(笑)を持ったアイテム(StackStringオブジェクト)がStackに積まれる。

そうしておいて、(3)の

With strStack
  Debug.Print .Top
End With

で、Topプロパティの値を出力する。この時点でStackのてっぺんにあるアイテムの値は先ほどの「1番サード岩鬼」のはず。

同様に、二つ目、三つ目、四つ目のアイテムを追加していく。

ここで、一旦(4)の

Debug.Print "===================="

で、イミディエイト ウインドウに区切り線を入れる。

後は、(5)の

With strStack
  Do While .ItemExists
    Debug.Print .Pop
  Loop
End With

で、ItemExistsプロパティがFalseになるまで、すなわちStackに積まれたアイテムが無くなるまで、〈てっぺんのアイテムの値を出力=てっぺんのアイテムを除去〉を繰り返す。

リスト1を実行すると、

f:id:akashi_keirin:20191109220856j:plain

Stackの場合、上へ上へと積み重ねたものを、上から順番に取り出すことになるので、こうなる。

おわりに

詳しい仕組みの説明は今のところ省略しますが、配列もCollectionも使わずに、データの集合を表現するって、何かスゴくないですか?

私はちょっと感動しています。

StackStringオブジェクトの内部にStackStringオブジェクトを持たせることによって、マトリョーシカ人形のような要領で複数のデータを数珠つなぎのように持たせる、ということなんですが、非常に面白いアイディアだと思います。

この調子で、Queueとか、OrderedLinkedListなんかも作っていくと、クラスモジュールの練習に良さそうです。