【テクニカル・上級編】【スタック構造活用】System.Collections.Stack を利用した LIFO(後入れ先出し)型処理履歴と逆順リスト操作 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

VBScriptを掌握する極限の知見:System.Collections.StackによるLIFO構造の完全制御

レガシーシステムの深部、あるいはWindows環境における軽量な自動化スクリプトの最前線において、VBScript(Visual Basic Scripting Edition)は今なお現役のインフラストラクチャとして静かに、しかし強烈に稼働し続けている。

WSH(Windows Script Host)の制約下において、開発者が直面する最大の壁は「データ構造の貧弱さ」である。標準で用意されているのは`Scripting.Dictionary`と固定・可変長配列(`Array`)のみ。ツリー構造の走査、深さ優先探索(DFS)、あるいは直感的な「UNDO(やり直し)機能」を実装しようとしたとき、貧弱な配列の再定義(`ReDim Preserve`)に頼るアプローチは、$O(N^2)$ のメモリ再割り当て地獄を引き起こし、パフォーマンスを完全に崩壊させる。

この限界を突破する鍵が、COMインターフェースを介した.NET Frameworkのランタイム(mscorlib)の直接召喚である。

今回は、`System.Collections.Stack`をVBScriptからインスタンス化し、極限まで最適化されたLIFO(Last-In, First-Out:後入れ先出し)型処理履歴と逆順リスト操作を実装する極意を授けよう。

—

1. なぜ VBScript で `.NET Stack` なのか?

VBScriptの標準機能だけでLIFOを実装する場合、配列の末尾をインデックスで管理するか、`ReDim Preserve`で要素を追加・削除することになる。しかし、`ReDim Preserve`は実行のたびにメモリ上の別領域へ全体をコピーするため、データ量が数千件を超えたあたりからパフォーマンスが急激に劣化する。

一方、`.NET Framework`の `System.Collections.Stack` は、内部的にオブジェクトの配列(参照の配列)を保持し、容量が溢れた際のみ倍のサイズで再割り当てを行う「償却計算量 $O(1)$」のアルゴリズムで設計されている。

これをVBScriptから`CreateObject(“System.Collections.Stack”)`で呼び出すことで、レガシーな文法でありながらモダンなデータ構造の恩恵を完全に受けることができるのだ。

—

2. 実装コード:LIFO履歴管理と逆順リスト操作の極意

以下のコードは、ファイルパスの処理履歴をスタックに積み上げ、エラー発生時に直前の操作へ即座にロールバック(UNDO)する処理、およびスタックの特性を利用した文字列・データの逆順出力(リバース)を行う実用スクリプトである。

‘ ==============================================================================
‘ スクリプト名: StackManager.vbs
‘ 概要: System.Collections.Stack を利用した高度なLIFO処理と逆順操作の實例
‘ アーキテクトノート: オブジェクトのライフサイクル管理とメモリ解放の模範実装
‘ ==============================================================================

Option Explicit

Sub Main()
Dim objStack
Dim vItem

WScript.Echo “=== 1. System.Collections.Stack の初期化 ===”
‘ COM経由で .NET の Stack クラスをインスタンス化
Set objStack = CreateObject(“System.Collections.Stack”)

If objStack Is Nothing Then
WScript.Echo “[FATAL] .NET Framework がロードされていません。”
Exit Sub
End If

‘ — LIFO (後入れ先出し) によるデータの蓄積 (Push) —
WScript.Echo vbCrLf & “=== 2. トランザクション履歴の Push (記録) ===”
objStack.Push “C:\Logs\transaction_20231001.log”
objStack.Push “C:\Logs\transaction_20231002.log”
objStack.Push “C:\Logs\transaction_20231003.log”

‘ 現在のスタックの深さを確認 (Count プロパティ)
WScript.Echo “現在の履歴深度 (Count): ” & objStack.Count

‘ 最上部の要素を破壊せずに覗き見 (Peek)
WScript.Echo “直近の処理対象 (Peek): ” & objStack.Peek()

‘ — UNDO処理の実装 (Pop による取り出し) —
WScript.Echo vbCrLf & “=== 3. 異常発生時のUNDO(ロールバック)処理 ===”
Do While objStack.Count > 0
‘ Pop は要素を取り出しつつ、スタックから削除する
vItem = objStack.Pop()
WScript.Echo “ロールバック実行 -> 破棄対象: ” & vItem
Loop

‘ — 応用編:文字列の逆順操作 (リバース処理) —
WScript.Echo vbCrLf & “=== 4. 応用:スタックを用いた文字列の逆順展開 ===”
Dim targetString, i, charArray
targetString = “VBSCRIPT ARCHITECTURE”

‘ 1文字ずつバラしてスタックに積む
For i = 1 To Len(targetString)
objStack.Push Mid(targetString, i, 1)
Next

‘ LIFOの性質により、取り出すと完全に逆順になる
Dim reversedString
reversedString = “”
Do While objStack.Count > 0
reversedString = reversedString & objStack.Pop()
Loop

WScript.Echo “元文字列 : ” & targetString
WScript.Echo “逆順展開後 : ” & reversedString

‘ ==========================================================================
明示的メモリ解放 (Memory Clean-up)
‘ ==========================================================================
‘ VBScriptのガベージコレクタはCOMオブジェクトの参照解放遅延を引き起こすことがある。
‘ 大規模バッチ処理では明示的な Nothing 代入がメモリリークを防ぐ鉄則。
Set objStack = Nothing
WScript.Echo vbCrLf & “=== 5. 処理正常終了:リソース解放完了 ===”

End Sub

‘ エントリポイントの呼び出し
Main()

—

3. チーフアーキテクトが解説する「知見と罠」

このコードを現場に投入するにあたり、シニアエンジニアとして知っておくべき「実装上の急所」を解説する。

① COM相互運用性と型のバインディング

VBScriptから`.NET`のオブジェクトを操作する場合、変数型はすべてバリアント型(`Variant`)として扱われる。`Stack.Push()` に渡されたデータは、COM境界を越える際に自動的にVARIANT構造体にラップされる。そのため、文字列、数値、果ては別のCOMオブジェクト(`FileSystemObject`等)ですら、同一のスタック混在して格納できる。ただし、型安全性が担保されないため、取り出す際の型アサーションやエラーハンドリングはスクリプト側で厳重に行うべきである。

② メモリ最適化とオブジェクトのライフサイクル

VBScriptの自動ガベージコレクション(引用カウント方式)は、循環参照やCOMオブジェクトの解放において完全ではない。特にWSHスクリプトがタスクスケジューラ等で常駐・大量実行される環境では、`Set objStack = Nothing` を省略すると、わずかなメモリリークが蓄積し、やがてサーバーのメモリ枯渇(OutOfMemory)を引き起こす。「使ったら捨てる。スコープを抜ける前には必ず `Nothing` を代入する」これはVBScriptにおける宗教的戒律である。

③ 例外処理(Error Handling)の重要性

レガシー環境やネットワーク越しの一時領域でこのスクリプトを動かす場合、`.NET`ランタイムのロード失敗や、空のスタックに対して誤って `Pop()` / `Peek()` を実行した際の `System.InvalidOperationException`(スタックが空です)が、VBScript側で捕捉されずにスクリプトの異常終了(ダイアログ表示によるフリーズ)を招く。
本番環境に投入する際は、必ず `On Error Resume Next` を適切に配置し、`Err.Number` のトラップを実装すること。

—

総括

VBScriptは「古い言語」として片付けられがちだが、OSの深部(Windows APIや.NET Framework)へのパイプラインとして捉え直した途端、その評価は一変する。貧弱な言語仕様を嘆くのではなく、外部の強力なコンポーネント(今回であれば `System.Collections.Stack`)をアセンブルしてアーキテクチャを補完する――これこそが、真のレガシーシステム・エンジニアリングの極意である。

現場の自動化スクリプトに「真のデータ構造」と「堅牢性」を実装し、システムの寿命をさらに引き延ばしてほしい。

タイトルとURLをコピーしました