ハッシュ実装

連想配列と言う言葉をご存知だろうか。Perl使いにはハッシュの名でお馴染みの概念で、吉里吉里使いも知っているのではないだろうか。
この連想配列を、速度は度外視してNScripterで実装してみた。文字列変数を一つ使って、それに文字列でも数値でもキーと結びつけて格納できるようにした。
スクリプトはこんな感じである。

 *define

defsub hash_read ; 指定したハッシュから値を読み出す。
defsub hash_exist ; 指定したハッシュに指定したキーが存在するかどうか。
defsub hash_mov ; 指定したハッシュに値を書き込む
defsub hash_write ; hash_movのエイリアス
defsub hash_delete ; 指定したハッシュからそのキーを消去する。
defsub hash_mov_fast ; 指定したキーが存在しない場合の高速書き込み
defsub hash_write_fast ; hash_mov_fastのエイリアス

defsub hash_read2 ; 得られた値を数値にして返す。
defsub hash_mov2 ; 数値を書き込む。
defsub hash_write2 ; hash_mov2のエイリアス
defsub hash_mov_fast2 ; 数値を高速に書き込む。
defsub hash_write_fast2 ; hash_mov_fast2のエイリアス

numalias hash_num,100
numalias hash_key,101
numalias hash_result,102
numalias hash_len,103
numalias hash_key_len,104
numalias hash_temp,105
numalias hash_loop,106
numalias hash_exist_result,107
numalias hash_value,108
numalias hash_delete_flag,109
numalias hash_result2,110

stralias hash_separater,"&"
stralias hash_separater2,"="

game

 *hash_read2
getparam i%hash_result2,s%hash_num,$hash_key ; 結果格納変数・対象文字列変数・読み出すハッシュキー
hash_read $hash_result2,$%hash_num,$hash_key
atoi %%hash_result2,$hash_result2
return

 *hash_read
getparam s%hash_result,s%hash_num,$hash_key ; 結果格納変数・対象文字列変数・読み出すハッシュキー
mov $%hash_result,"" ; 結果の初期化
hash_exist %hash_temp,$%hash_num,$hash_key ; そのキーがあるかどうかチェックする。
if %hash_temp<0 return ; 存在しないなら、空白を返す。
mov %hash_loop,%hash_key_len
 *hash_read_loop
inc %hash_loop
mid $hash_temp,$%hash_num,%hash_temp+%hash_loop,1
notif $hash_temp=hash_separater goto *hash_read_loop
mid $hash_temp,$%hash_num,%hash_temp+1,%hash_loop-1
split $hash_temp,hash_separater2,$hash_temp,$%hash_result
return

; 指定したハッシュに指定したキーが存在するかどうか。
; 返り値が-1なら、存在しない。それ以外なら何文字目かが入る。
 *hash_exist
getparam i%hash_exist_result,s%hash_num,$hash_key ; 結果格納変数・対象文字列変数・調査するハッシュキー
mov %%hash_exist_result,-1 ; 結果の初期化
len %hash_len,$%hash_num
if %hash_len=0 return ; ハッシュが初期化されているなら、すぐに戻る。
len %hash_key_len,$hash_key
if %hash_key_len=0 return ; 空白のキーはバグの元なのでちょっと……。
for %hash_loop=0 to %hash_len-%hash_key_len+1
	mid $hash_temp,$%hash_num,%hash_loop,1 ; 一文字取得
	notif $hash_temp=hash_separater jumpf ; それが&でなければ次のループ
	mid $hash_temp,$%hash_num,%hash_loop+1,%hash_key_len+1 ; その後のキーを取得。
	notif $hash_temp=$hash_key+hash_separater2 jumpf ; それがキー名+=でなければ次のループ
	; ヒットした。
	mov %%hash_exist_result,%hash_loop+1
	break
~
next
return

 *hash_mov2
 *hash_write2
getparam s%hash_num,$hash_key,%hash_value ; 対象の文字列変数・キー・新しい値
itoa $hash_value,%hash_value
goto *hash_mov_body

 *hash_mov
 *hash_write
getparam s%hash_num,$hash_key,$hash_value ; 対象の文字列変数・キー・新しい値
 *hash_mov_body
mov %hash_delete_flag,0
if $%hash_num="" mov $%hash_num,hash_separater
hash_exist %hash_temp,$%hash_num,$hash_key ; そのキーがあるかどうかチェックする。
if %hash_temp<0 goto *hash_mov_last
 *hash_delete_body
; 既存のキーがあるとちょっと面倒かな……。
mid $hash_temp,$%hash_num,0,%hash_temp-1
mov $hash_result,$hash_temp
 *hash_write_loop
inc %hash_temp
mid $hash_temp,$%hash_num,%hash_temp,1
notif $hash_temp=hash_separater goto *hash_write_loop
mid $hash_temp,$%hash_num,%hash_temp,%hash_len-%hash_temp
add $hash_result,$hash_temp
mov $%hash_num,$hash_result

; 文字列変数の最後に追加する。
 *hash_mov_last
if %hash_delete_flag=0 add $%hash_num,$hash_key+hash_separater2+$hash_value+hash_separater
mov %hash_delete_flag,0
return

 *hash_write_fast2
 *hash_mov_fast2
getparam s%hash_num,$hash_key,%hash_value ; 対象の文字列変数・キー・新しい値
itoa $hash_value,%hash_value
goto *hash_move_last

 *hash_write_fast
 *hash_mov_fast
getparam s%hash_num,$hash_key,$hash_value ; 対象の文字列変数・キー・新しい値
goto *hash_move_last

 *hash_delete
getparam s%hash_num,$hash_key
if $%hash_num="" mov $%hash_num,hash_separater
hash_exist %hash_temp,$%hash_num,$hash_key ; そのキーがあるかどうかチェックする。
if %hash_temp<0 return ; キーがなければ何もしない。
mov %hash_delete_flag,1 ; フラグを立てる。
goto *hash_delete_body

 *start
end

一つの文字列変数に対し、hash_readやhash_writeを通じてキーを添えてアクセスすることで、不特定多数の数値や文字列を一つの文字列変数に収めることができる。
意味がわかったら使ってみて欲しい。