最終更新日 :
VB Tips

結び目型コレクション(その2)

周りのサイトなどを見まわしてみると、「クラス」というと何か最新の機能をラップしたり、複雑なことを実現するためのモジュールのように使われがちですが、必ずしもそうでないということを言っているだけであってそのようなものを求めている方はつまらないだろうとは思いますが、しばらくの間お付き合いください。
もう一度書きますが、新しくオブジェクトを構築する時などの参考にして頂ければ幸いです。

プロパティ, メソッド(KnotManagerオブジェクト)
プロパティ :
Count(Get), FirstItem(Get), [FirstItem(Let)], LastItem(Get), [LastItem(Let)], Ladder, LadderBase
メソッド :
HasThisItem, CreateAndAttach, CreateNewItem, AttachListItems

[]の中のものはFriendプロシージャです。プロジェクト内で使用する場合、Publicに書き換えるのは自由です。

KnotItemオブジェクトの作成
1.アイテムの取得(その1の続き)
Public Function GetAt(ByVal Index As Long) As KnotItem
' 指定したインデックスのアイテムを返します。
Dim lb As Long, b As Long
With mManager
lb = .LadderBase
b = Abs(Index - mIndex) - lb
If (Index > .LastItem.Index) Or (Index < 1) Then
Set GetAt = Nothing
Exit Function
ElseIf b > 0 Then
lb = lb * (Index \ lb)
b = Abs(Index - lb)
If b >= .LadderBase / 2 Then lb = lb + .LadderBase * IIf(Index - lb < 0, -1, 1)
Select Case lb
Case Is > .LastItem.Index
Set GetAt = .LastItem.GetAt(Index)
Case Is < 1
Set GetAt = .FirstItem.GetAt(Index)
Case Else
Set GetAt = .Ladder("#" & CStr(lb)).GetAt(Index)
End Select
Exit Function
End If
End With

Select Case Index
Case mIndex
Set GetAt = Me
Case Is > mIndex
If mNext Is Nothing Then
Set GetAt = Nothing
Else
Set GetAt = mNext.GetAt(Index)
End If
Case Else
If mPrev Is Nothing Then
Set GetAt = Nothing
Else
Set GetAt = mPrev.GetAt(Index)
End If
End Select


End Function


このメソッドはマネージャのプロパティなどと大いに関係があるため、こちらに載せました。まず、前半(〜End With)と後半(Select Case〜)に分けて説明していきたいと思います。
前半では、簡単に言えば位置判定を行っています。 ラダー(Ladder : はしご)オブジェクトというコレクションをマネージャ内部に忍ばせていますが、これは検索スピードをアップさせるためのものです。 またあまり多くのKnotItemオブジェクトをつなげた場合、このメソッドでは再帰処理を行っているのでスタック不足に陥る可能性があるのでそのための対処ということもあります。
そして後半部分では現在の位置において検索すべき方向にある隣のKnotItemオブジェクトのGetAtメソッドを実行して、行き着くところ(KnotItemに格納されているIndexと引数のIndexが一致する)まで繰り返します。
具体例で説明してみましょう。左の図のような状態を考えてみます。 青の線で示した、GetAt()が実行されたアイテムと、目的とするアイテム(赤の線)のインデックスとの幅を調べ、その幅がラダーの間隔より大きいかどうか調べます。大きい場合、次のラダーコレクションの要素(矢印の方向にある黒い線)に一気に飛びます(前半部分)。そして、そこから一つ一つ矢印の方向に調べていくのです(後半部分)。
Friend Sub SetNumbers(ByVal NewIndex As Long, Optional ByVal NewManager As KnotManager)
Dim k As String

If NewIndex < 1 Then
Err.Raise 5
Exit Sub
End If
mIndex = NewIndex

' (前の項と)マネージャが違えば交換する
If Not (NewManager Is Nothing) Then
If Not (NewManager Is mManager) Then Set mManager = NewManager
ElseIf Not (mPrev Is Nothing) Then
If (Not (mManager Is mPrev.Manager)) And (Not (mPrev.Manager Is Nothing)) Then Set mManager = mPrev.Manager
End If

With mManager
If NewIndex = 1 Then
.FirstItem = Me
.Ladder = New Collection
ElseIf (NewIndex Mod .LadderBase) = 0 Then ' 30位が適当?
k = "#" & CStr(NewIndex)
On Error Resume Next
.Ladder.Add Me, k
If Err.Number <> 0 Then Set .Ladder(k) = Me
Err.Clear
On Error GoTo 0
End If
End With

If Not (mNext Is Nothing) Then
' 最後に達していなければ、次に回す
If Not (NewManager Is Nothing) Then
mNext.SetNumbers NewIndex + 1, NewManager
Else
mNext.SetNumbers NewIndex + 1
End If
Else
' 最後なら、最終アイテムを設定して終了
mManager.LastItem = Me
End If

End Sub


見て気づくかもしれませんが、このGetAt()メソッドとSetNumbers()では、再帰処理がふんだんに使われています。再帰処理(recursive process)は、検索や並べ替え、連続処理に対して有効な場合が数多くあります。
この関数は、じっくり見ればコメントが少なくてもきっと分かるはずです。じっくり解読してみてください。
2.マネージャのメソッド
CreateAndAttach, CreateNewItem, AttachListItemsがこのマネージャの主要なメソッドですが、これらメソッドがやっていることは全て同じです。 新しいKnotItemオブジェクトを作成して(AttachListItems()では作成しません)自らのFirstItemプロパティにこの新しく作った(AttachListItems()では指定された)アイテムを設定する。そして、SetNumbers()メソッドで番号を整理しているだけなのです。
簡単ですが、本当にこれだけのことなのです。
次回は、利用例を載せることができれば、と思っています。

Copyright (C) 1999 Satoshi Tadaフィードバックはこちら
Home