UWSCでスタックとキュー処理

UWSCには連想配列はあるけど、スタックやキューがない。
スタックは連想配列を使えば簡単なので、スレッドセーフなものを考えてみた。
で、Pushしてから先頭を取り出せば、Queueになるので、Stackモジュールだけど、キューも実装した。



スクリプト

Stack.uws

OPTION EXPLICIT

IFB GET_UWSC_NAME = "Stack.uws" THEN
	DIM i
	FOR i = 10 TO 21
		Stack.Push(i)
	NEXT
	PRINT Stack.Dump()
	PRINT "Stack.Top " + Stack.Top()
	PRINT "Stack.Pop " + Stack.Pop()
	PRINT Stack.Dump()
	Stack.Dispose()
	Stack.Stack

	FOR i = 0 TO 9
		Stack.Push(i)
	NEXT
	FOR i = 10 TO 1034
		Stack.Push(i)
		IF Stack.Top() <> i - 10 THEN PRINT "NG " + i
	NEXT
	PRINT Stack.Dump()
	Stack.Top()
	Stack.Push(1034)
	PRINT Stack.Dump()

	Stack.Dispose()
ENDIF


MODULE Stack
	HASHTBL _stack
	DIM _event = NULL

	DEF_DLL CloseHandle(DWORD): BOOL: kernel32
	DEF_DLL WaitForSingleObject(DWORD,DWORD): DWORD: kernel32
	DEF_DLL CreateEventW(DWORD,BOOL,BOOL,wstring): DWORD: kernel32
	DEF_DLL SetEvent(DWORD): BOOL: kernel32

	CONST WAIT_TIMEOUT = 258
	CONST REP_NUM = 1024

	PROCEDURE Stack
		IFB _event = NULL THEN
			_event = CreateEventW(NULL, FALSE, TRUE, NULL)
		ENDIF
	FEND

	PROCEDURE Dispose()
		_stack = HASH_REMOVEALL
		CloseHandle(_event)
		_event = NULL
	FEND

	FUNCTION Wait(timeout=$7FFFFFFF, waitUnit=100)
		RESULT = WAIT_TIMEOUT
		WHILE RESULT = WAIT_TIMEOUT AND timeout > 0
			RESULT = WaitForSingleObject(_event, waitUnit)
			timeout = timeout - waitUnit
		WEND
	FEND

	FUNCTION Push(obj)
		Wait()
		RESULT = LENGTH(_stack)
		IF RESULT THEN RESULT = VAL(_stack[RESULT - 1, HASH_KEY]) + 1
		_stack[RESULT] = obj
		SetEvent(_event)
	FEND

	FUNCTION Pop(emp=EMPTY)
		Wait()
		RESULT = emp
		DIM key = LENGTH(_stack)
		IFB key THEN
			key = _stack[key - 1, HASH_KEY]
			RESULT = _stack[key]
			key = _stack[key, HASH_REMOVE]
		ENDIF
		SetEvent(_event)
	FEND

	FUNCTION Top(emp=EMPTY)
		Wait()
		RESULT = emp
		DIM i, key = LENGTH(_stack)
		IFB key THEN
			key = VAL(_stack[0, HASH_KEY])
			RESULT = _stack[key]
			i = _stack[key, HASH_REMOVE]
			IFB key > REP_NUM THEN
				HASHTBL tmp
				FOR i = 0 TO LENGTH(_stack) - 1
					tmp[i] = _stack[i, HASH_VAL]
				NEXT
				_stack = HASH_REMOVEALL
				FOR i = 0 TO LENGTH(tmp) - 1
					_stack[i] = tmp[i]
				NEXT
			ENDIF
		ENDIF
		SetEvent(_event)
	FEND

	FUNCTION Size()
		RESULT = LENGTH(_stack)
	FEND

	FUNCTION Dump()
		Wait()
		RESULT = "Stack Dump<#CR>"
		DIM i
		FOR i = 0 TO LENGTH(_stack) - 1
			RESULT = RESULT + " " + _stack[i, HASH_KEY] + " : " + _stack[i, HASH_VAL] + "<#CR>"
		NEXT
		SetEvent(_event)
	FEND

ENDMODULE


Pushすれば最後に積みます。
Popすると最後を取り出します。
Topすると先頭を取り出します。
Stackを使い終わったら、Dispose。
Dispose後再度使いたい場合は、Stack.Stackを呼んでください。

原理

Pushは、連想配列に0から積むようにしています。
空なら0で積んで、空でない場合は、最後に積んだ番号+1で積む。


Popは、最後を取り出します。


Topは、先頭を取り出しますが、さらに、開始番号が指定数より大きくなったら積みなおしを行います。
(もうちょっと工夫した方が良い気もしますが)


Pop/Topの第一引数は、なかった場合のリターン。
初期EMPTYだけど、スタックにEMPTYを積みたい場合に備えて用意しています。

課題

スタックを一つしかもてません。
複数持ちたい場合、連想配列もどきを使えば良いかと思います。
UWSCで連想配列を配列にする - じゅんじゅんのきまぐれ
各関数が名前を引数にとって、連想配列連想配列もどきをその名前で入れればOK。