配列もCollectionも使わないデータ構造(Queue)
配列もCollectionも使わないデータ構造(Queue)
前回、前々回
のStackに続いて、Queueも作った。
コードを簡単にするために、今回もString
型データ専門。
値なら何でもオッケーにしたければ、Variant
にしてください。
StringQueueクラスとStringQueueItemクラス
用意するのは標題の二つのクラス。とりあえずコードを掲載。
クラスモジュール StringQueue
Option Explicit 'Field Variables' Private frontItem As StringQueueItem Private rearItem As StringQueueItem Private count_ As Long 'Properties' Public Property Get ItemExists() As Boolean ItemExists = Not ((frontItem Is Nothing) And (rearItem Is Nothing)) End Property Public Property Get Count() As Long Count = count_ End Property Public Property Get Item(ByVal Index As Long) As StringQueueItem Dim ret As StringQueueItem Set ret = Nothing 'IndexがおかしかったらNothingを返す' If Index < 1 Then GoTo Finalizer If Index > Me.Count Then GoTo Finalizer 'アイテムがあったら返す' If ItemExists Then Set ret = frontItem 'アイテムが一つだけなら先頭のアイテムを返す' If Me.Count = 1 Then GoTo Finalizer Dim i As Long For i = 2 To Index Set ret = ret.NextItem Next End If Finalizer: Set Item = ret End Property Public Property Get Front() As StringQueueItem Dim ret As StringQueueItem Set ret = Nothing If ItemExists Then Set ret = frontItem Set Front = ret End Property Public Property Get Rear() As StringQueueItem Dim ret As StringQueueItem Set ret = Nothing If Me.ItemExists Then Set ret = rearItem Set Rear = ret End Property 'Constructor' Private Sub Class_Initialize() count_ = 0 End Sub 'Methods' Public Sub addItem(ByVal newValue As String) '新しいStringQueueItemオブジェクトを作る' Dim newStringQueueItem As New StringQueueItem 'StringQueueItemオブジェクトのValueプロパティをセット' newStringQueueItem.Value = newValue If Me.ItemExists Then '追加前のStringQueueにアイテムがあったとき' Set rearItem.NextItem = newStringQueueItem Set rearItem = newStringQueueItem Else 'アイテム追加前のStringQueueが空だったとき' Set frontItem = newStringQueueItem Set rearItem = newStringQueueItem End If count_ = count_ + 1 End Sub Public Function removeItem() As StringQueueItem Dim ret As StringQueueItem Set ret = Nothing 'アイテムがあったらアイテムをセット' If Me.ItemExists Then Set ret = frontItem 'アイテム除去後の後始末' If frontItem Is rearItem Then 'アイテムが一つだけだったら、StringQueueは空になる' Set frontItem = Nothing Set rearItem = Nothing Else '先頭のアイテムを入れ換える' Set frontItem = frontItem.NextItem End If count_ = count_ - 1 End If Set removeItem = ret End Function
StringStack
クラスの場合は、一番上のアイテムへの参照であるTop
プロパティだけで良かった(データの出し入れが一箇所だけなので。)が、StringQueue
クラスの場合は、データの入口と出口が異なるので、データ集合の先頭アイテムへの参照であるFront
プロパティと、データ集合の最後尾のアイテムへの参照であるRear
プロパティが必要となる。
新しくアイテムを追加する(つまり、最後尾にアイテムを追加する)ためのaddItem
メソッドと、先頭のアイテムを取得するとともに削除するremoveItem
メソッドの中身を見てもらえば、どのような処理をしているのかがおわかりかと思う(わかりにくければ、コメント欄とか、Twitter、ノンプロ研Slackなんかに質問プリーズ。)。
クラスモジュール StringQueueItem
Option Explicit 'Field Variables' Private value_ As String Private nextItem_ As StringQueueItem 'Properties' 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 StringQueueItem) Set nextItem_ = argItem End Property Public Property Get NextItem() As StringQueueItem Set NextItem = nextItem_ End Property
こちらの方は至ってシンプル。というか、前回のStringStackItem
クラスとほとんど同じ。まあ、Stackにしても、Queueにしても一列棒状にアイテムがつながっている、というデータ構造なので、次のアイテムへの参照(NextItem
プロパティ。StringQueueItem
型。)を持たせておけば、それだけで表現できるのであった。
使ってみる
次のコードで使ってみる。
リスト1 標準モジュール
Private Sub testStringQueue() Private strQueue As New StringQueue With strQueue Call .addItem("1番サード岩鬼") '……(1)' Debug.Print .Front.Value & " ~ "; .Rear.Value Debug.Print "====================" Call .addItem("2番セカンド殿馬") Debug.Print .Front.Value & " ~ "; .Rear.Value Debug.Print "====================" Call .addItem("3番レフト微笑") Debug.Print .Front.Value & " ~ "; .Rear.Value Debug.Print "====================" Call .addItem("4番キャッチャー山田") Debug.Print .Front.Value & " ~ "; .Rear.Value Debug.Print "====================" Dim i As Long '……(2)' For i = 1 To 4 Debug.Print "前から" & CStr(StrConv(i, vbWide)) & _ "番目のアイテムは、" & _ .Item(i).Value & "です。" Next Debug.Print "====================" '……(3)' Do While .ItemExists '……(4)' Debug.Print .removeItem.Value & " を削除しました。" Debug.Print "現在の保有アイテム数は、" & _ .Count & " 個です。" Loop End With End Sub
(1)の
With strQueue Call .addItem("1番サード岩鬼") Debug.Print .Front.Value & " ~ "; .Rear.Value Debug.Print "====================" Call .addItem("2番セカンド殿馬") Debug.Print .Front.Value & " ~ "; .Rear.Value Debug.Print "====================" Call .addItem("3番レフト微笑") Debug.Print .Front.Value & " ~ "; .Rear.Value Debug.Print "====================" Call .addItem("4番キャッチャー山田") Debug.Print .Front.Value & " ~ "; .Rear.Value End With
では、addItem
メソッドを用いてアイテムを追加しつつ、そのたびに先頭のアイテムの値と最後尾のアイテムの値を出力。あと、アイテムごとに区切り線も追加している。
四つのアイテムを追加し終えると、(2)の
With strQueue Dim i As Long For i = 1 To 4 Debug.Print "前から" & CStr(StrConv(i, vbWide)) & _ "番目のアイテムは、" & _ .Item(i).Value & "です。" Next End With
で、先頭のアイテムから順に値を出力。
(3)の
Debug.Print "===================="
で区切りを入れて、あとは(4)の
With strQueue Do While .ItemExists Debug.Print .removeItem.Value & " を削除しました。" Debug.Print "現在の保有アイテム数は、" & _ .Count & " 個です。" Loop End With
で、アイテムのある限り先頭アイテムの値を出力し、そのたびに残りのアイテム数を出力する。
実行結果
リスト1の実行結果は
このとおり。
おわりに
実に面白い。
この要領で、たとえば、OrderdLinkedList
も作ることができる。
速度面でどうなのかはわからないが。