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

スポンサーリンク

【VBScriptを極める】`System.Collections.Stack` で実現するLIFO構造の真髄:逆順処理とUNDO設計の極意

開発現場において、VBScriptは今なおレガシーシステムとの統合や、キッティングなどのローカル環境自動化において強力なカードであり続けている。しかし、プリミティブな配列(`Array`)や可変長配列(`ReDim Preserve`)のメモリ管理コストの高さに苦しめられた経験はないだろうか。

特に、要素の追加と削除が頻発する履歴管理、例えば「処理のUNDO(やり直し)機能」や、ディレクトリ構造の「深さ優先探索(DFS)」を素朴な配列で実装しようとすると、コードは肥大化し、パフォーマンスは悲惨なことになる。

今回は、COMの相互運用性を利用して.NET Frameworkの `System.Collections.Stack` をVBScriptから直接召喚し、堅牢かつ圧倒的なパフォーマンスを持つLIFO(Last-In, First-Out:後入れ先出し)型データ構造を構築する技術を伝授する。

—

なぜ「ReDim Preserve」による配列操作は実務で破綻するのか?

多くの初学者、あるいは設計思想を軽視したプログラマは、VBScriptで履歴を管理する際に以下のようなコードを書く。

‘ 【アンチパターン】配列による履歴管理の悪夢
Dim history()
ReDim history(0)
history(0) = “Step 1”

‘ 要素を追加するたびに ReDim Preserve
ReDim Preserve history(UBound(history) + 1)
history(UBound(history)) = “Step 2”

このアプローチがなぜ実務の現場でタブー視されるのか?理由は明確だ。
`ReDim Preserve` を実行するたびに、裏で「メモリ領域の再割り当て」と「既存要素の全コピー」が発生する。要素数が数千件規模に達した瞬間、計算量は $O(N^2)$ に跳ね上がり、スクリプトはフリーズしたかのような挙動を示す。

さらに、LIFO構造が必要な場面(直前の操作を取り消す等)において、配列の末尾を切り詰める処理はコードの可読性を著しく下げる。

解決策:.NETの `System.Collections.Stack` を使う

VBScript自体には高度なデータ構造が不足しているが、我々にはWindows環境の基盤である COM/.NET Interop がある。これを利用して `System.Collections.Stack` をインスタンス化すれば、メモリの動的管理はすべて.NET側に委譲され、VBScript側からは洗練されたインターフェースだけを叩くことができる。

—

プロダクションコード:堅牢なUNDO機能と逆順履歴処理

以下のコードは、ファイル処理のログや操作履歴を `Stack` で管理し、エラー発生時に逆順でロールバック(UNDO)を行う実用的なモジュールだ。

Option Explicit

‘ ==============================================================================
‘ スクリプト名: StackHistoryManager.vbs
‘ 概要: System.Collections.Stack を利用したLIFO型履歴管理と逆順処理の実装例
‘ ==============================================================================

Main

Sub Main
WScript.Echo “=== LIFO Stack 処理履歴・UNDOデモ開始 ===”

‘ 1. Stack オブジェクトの生成
Dim undoStack
Set undoStack = CreateObject(“System.Collections.Stack”)

‘ 2. 処理の実行と履歴(UNDOトークン)のプッシュ
‘ ※実務では、ファイル作成やレジストリ変更の「逆操作コマンド」を積んでいく
WScript.Echo vbCrLf & “[1] 操作を実行し、逆順処理用のUNDOトークンを積み上げます…”

Call ExecuteAndPush(undoStack, “ファイルを生成しました: C:\Temp\log_3.txt”, “DELETE_FILE:C:\Temp\log_3.txt”)
Call ExecuteAndPush(undoStack, “ファイルを生成しました: C:\Temp\log_2.txt”, “DELETE_FILE:C:\Temp\log_2.txt”)
Call ExecuteAndPush(undoStack, “ファイルを生成しました: C:\Temp\log_1.txt”, “DELETE_FILE:C:\Temp\log_1.txt”)

‘ 現在のスタックの状態(深さ)を確認
WScript.Echo “-> 現在の未処理UNDOスタック数: ” & undoStack.Count

‘ 3. 異常発生を想定したロールバック(UNDO)処理の実行
WScript.Echo vbCrLf & “[2] 予期せぬエラーが発生しました。スタックからLIFOで取り出しロールバックします…”

Do While undoStack.Count > 0
‘ Pop で最新の履歴から順に取り出す(後入れ先出し)
Dim undoAction
undoAction = undoStack.Pop()

‘ ロールバックの実処理をシミュレート
Call RollbackOperation(undoAction)
Loop

WScript.Echo vbCrLf & “=== すべての処理が正常に完了しました ===”
End Sub

‘ 操作を実行し、UNDO用データをStackに積む関数
Sub ExecuteAndPush(ByRef stackObj, ByVal operationLog, ByVal undoToken)
WScript.Echo ” [EXEC] ” & operationLog
‘ スタックに逆操作用トークンをプッシュ
stackObj.Push(undoToken)
End Sub

‘ ロールバックを実行する関数
Sub RollbackOperation(ByVal undoToken)
‘ トークンを解析して逆直の処理を行う
Dim parts
parts = Split(undoToken, “:”)

Select Case parts(0)
Case “DELETE_FILE”
WScript.Echo ” [UNDO] 実ファイルを削除します -> ” & parts(1)
‘ ここに実際の FileSystemObject による削除処理を入れる
‘ Dim fso: Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ If fso.FileExists(parts(1)) Then fso.DeleteFile(parts(1))

Case Else
WScript.Echo ” [UNDO] 不明なロールバック命令: ” & undoToken
End Select
End Sub

—

アーキテクトが教える:実務導入時の致命的な罠と回避策

この手法を実際の業務自動化ツールに組み込む際、熟練エンジニアであってもハマる罠が存在する。以下の3点を必ず押さえておいてほしい。

1. COMオブジェクトの型変換とバリアントの罠

`System.Collections.Stack` の `Push` メソッドは `System.Object` を受け取るため、VBScriptのバリアント型は自動的に .NET の型(文字列なら `String` 等)にラップされる。しかし、参照渡し(`ByRef`)でスタックを関数に渡す際は、必ず `ByRef` を明示すること。これを怠ると、意図せず値渡し(コピー)が発生し、データが同期されなくなる。

2. 例外ハンドリング(`On Error Resume Next`)との共存

VBScriptでCOMオブジェクトを扱う以上、予期せぬエラーへの備えは必須だ。特に.NETコンポーネント側で例外が発生した場合、VBScript側には `Err` オブジェクトとして返却される。
スタック操作自体で落ちることは稀だが、ファイルI/Oやデータベース接続と組み合わせる場合は、以下のようにトランザクション的な発想でスタックを活用せよ。

On Error Resume Next
‘ 処理本体…
If Err.Number <> 0 Then
‘ エラー発生時は即座にスタックからポップしてロールバックを実行
Do While undoStack.Count > 0
RollbackOperation(undoStack.Pop())
Loop
WScript.Echo “致命的エラーによりロールバックしました: ” & Err.Description
End If
On Error GoTo 0

3. メモリリークの防止とオブジェクトの解放

VBScriptのガベージコレクションは参照カウント方式をベースとしている。処理が終了した時点で、`Set undoStack = Nothing` を明示的に呼び出し、COMオブジェクトの参照を確実に解放する習慣をつけよ。これを怠ると、IISからの呼び出し(ASP環境)やタスクスケジューラでの常駐実行時にメモリフットプリントが肥大化する原因となる。

—

まとめ

VBScriptという一見古びた言語であっても、背後にあるOSのアーキテクチャ(.NET Framework / COM)を正しく理解し、適切なデータ構造(`System.Collections.Stack`)を組み合わせることで、モダン言語に匹敵する堅牢で美しいLIFO処理を実装することが可能だ。

「配列を `ReDim` する」という悪習を断ち切り、スタック構造によるエレガントな履歴管理をあなたの業務自動化スクリプトに導入してほしい。コードの信頼性は劇的に向上するはずだ。

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