YAYAで連想配列が使えないと聞いたので簡単なのを実装してみた。

どれが速いかわからないので数種類やってみた。

  • バグがあったら報告してください。
  • ライセンスは一応CC0とかで。

ハッシュ:1つの連想配列メモリを使ってIDで区別する実装(微妙そう)

EVALと巨大な引数渡しを回避する方法

利点

  • EVAL等の遅そうな処理がない

欠点

  • 実行時全体のハッシュが一堂に会するので、要素数が多くなった場合微妙かも
  • EVAL等がない代わりにロジックは煩雑
  • YAYAが変数を自動保存するので終了時に内部変数を削除しなければならない
  • 操作関数の返り値を返さないのが面倒

使用例

// map__hash_size = 65536 // 確保するハッシュ領域のサイズ 実行中一度決めたら変更しない
_m = map_new() // 初期化してIDを取得
_junk = map_set(_m, 'key', 'value')
_k = map_get(_m, 'key')
_ks = map_keys(_m)
_junk = map_delete(_m, 'key')
_junk = map_destroy(_m) // 削除して使えるハッシュ領域を確保
_junk = map_destroy_all() // ハッシュ領域のグローバル変数開放

実装

/*
	_map_id = map_new()
	連想配列初期化
	cf. map = {}
*/
map_new{
	if ! ISVAR('map__hash_size') {
		map__hash_size = 65536
	}
	if ! ISVAR('map__id_num') {
		map__id_num = 0
	}
	if ! ISVAR('map__key_lists') {
		map__key_lists = IARRAY
	}
	if ! ISVAR('map__keys') {
		map__keys = IARRAY
	}
	if ! ISVAR('map__values') {
		map__values = IARRAY
	}
	map__id_num
	map__id_num++
}

/*
	_value = map_set(_map_id, _key, _value)
	連想配列_map_idの_keyに_valueをセット
	cf. map{key} = value
*/
map_set{
	_id = _argv[0]
	_key = _argv[1]
	_value = _argv[2]
	_hash = map__get_only_index(_id, _key)
	// キーが事前に存在しないならmap__key_listsにキー情報を追加
	if map__keys[_hash] == IVOID {
		map__key_lists = (map__key_lists, _id, _key)
	}
	map__keys[_hash] = _key
	map__values[_hash] = _value
	_value
}

/*
	_value = map_get(_map_id, _key)
	連想配列_map_idの_keyを参照
	cf. value = map{key}
*/
map_get{
	_id = _argv[0]
	_key = _argv[1]
	_hash = map__get_only_index(_id, _key)
	map__values[_hash]
}

/*
	_junk = map_delete(_map_id, _key)
	連想配列_map_idの_keyを削除
	cf. delete(map{key})
*/
map_delete{
	_id = _argv[0]
	_key = _argv[1]
	_hash = map__get_only_index(_id, _key)
	map__keys[_hash] = IVOID
	map__values[_hash] = IVOID
}

/*
	_keys = map_keys(_map_id, _key)
	連想配列_map_idの_keyを列挙
	cf. keys = keys(map{key})
*/
map_keys{
	_id = _argv[0]
	_size = ARRAYSIZE(map__key_lists) / 2
	_keys = IARRAY
	for _i = 0; _i < _size; _i++ {
		if map__key_lists[_i * 2] == _id {
			_key = map__key_lists[_i * 2 + 1]
			_hash = map__get_only_index(_id, _key)
			if map__values[_hash] != IVOID {
				_keys = (_keys, _key)
			}
		}
	}
	_keys
}

/*
	_junk = map_destroy(_map_id)
	連想配列_map_idを削除
*/
map_destroy{
	// keysによりmap__keys, map__values当該を消してからmap__key_lists当該を消す
	_id = _argv[0]
	_keys = map_keys(_id)
	foreach _keys; _key {
		_hash = map__get_only_index(_id, _key)
		map__keys[_hash] = IVOID
		map__values[_hash] = IVOID
	}
	_size = ARRAYSIZE(map__key_lists) / 2
	_to_delete = IARRAY
	for _i = 0; _i < _size; _i++ {
		if map__key_lists[_i * 2] == _id {
			_to_delete = (_to_delete, _i)
		}
	}
	_to_delete = ASORT('int,descending', _to_delete)
	foreach _to_delete; _i {
		map__key_lists[_i * 2, _i * 2 + 1] = IARRAY
	}
}

/*
	_junk = map_destroy_all()
	連想配列が使用する変数を削除
*/
map_destroy_all{
	ERASEVAR('map__hash_size', 'map__id_num', 'map__key_lists', 'map__keys', 'map__values')
}

map__get_only_index{ // 重複しないようインデックスをずらす
	_id = _argv[0]
	_key = _argv[1]
	_hash_key = _id + TOSTR(_key)
	_hash = map__hash(_hash_key)
	while (map__keys[_hash] != IVOID) && (map__keys[_hash] != _key) {
		if _hash >= (map__hash_size - 1){
			_hash = 0
		} else {
			_hash++
		}
	}
	_hash
}

map__hash{ // ハッシュ関数
	_key = _argv[0]
	_key_len = STRLEN(_key)
	_hash = 0
	for _i = 1; _i < _key_len; _i++ {
		_hash = (_hash * 137 + CHRCODE(_key, _i)) % map__hash_size
	}
	_hash
}

ハッシュ:EVALによる実装(あり?)

EVALを使う代わりにより自然なロジックで実装できる。

利点

  • 自然なロジックで実装できる

欠点

  • EVALが遅そう(実用上そう気になるレベルではない気がする)
  • 変数の削除はやはり必要。
  • 操作関数の返り値を返さないのが面倒

使用例

// map__hash_size = 2048 // 一連想配列に確保するハッシュ領域のサイズ 実行中一度決めたら変更しない
_m = map_new() // 初期化してIDを取得
_junk = map_set(_m, 'key', 'value')
_k = map_get(_m, 'key')
_ks = map_keys(_m)
_junk = map_delete(_m, 'key')
_junk = map_destroy(_m) // 変数開放

実装

/*
	_map_id = map_new()
	連想配列初期化
	cf. map = {}
*/
map_new{
	if ! ISVAR('map__hash_size') {
		map__hash_size = 2048
	}
	if ! ISVAR('map__id_num') {
		map__id_num = 0
	}
	LETTONAME('map__keys_' + map__id_num, IARRAY)
	LETTONAME('map__values_' + map__id_num, IARRAY)
	map__id_num
	map__id_num++
}

/*
	_value = map_set(_map_id, _key, _value)
	連想配列_map_idの_keyに_valueをセット
	cf. map{key} = value
*/
map_set{
	_id = _argv[0]
	_key = _argv[1]
	_value = _argv[2]
	_hash = map__get_only_index(_id, _key)
	_junk = EVAL('map__keys_' + map__id_num + '[_hash] = _key')
	_junk = EVAL('map__values_' + map__id_num + '[_hash] = _value')
	_value
}

/*
	_value = map_get(_map_id, _key)
	連想配列_map_idの_keyを参照
	cf. value = map{key}
*/
map_get{
	_id = _argv[0]
	_key = _argv[1]
	_hash = map__get_only_index(_id, _key)
	EVAL('map__values_' + map__id_num + '[_hash]')
}

/*
	_junk = map_delete(_map_id, _key)
	連想配列_map_idの_keyを削除
	cf. delete(map{key})
*/
map_delete{
	_id = _argv[0]
	_key = _argv[1]
	_hash = map__get_only_index(_id, _key)
	_junk = EVAL('map__keys_' + map__id_num + '[_hash] = IVOID')
	_junk = EVAL('map__values_' + map__id_num + '[_hash] = IVOID')
}

/*
	_keys = map_keys(_map_id, _key)
	連想配列_map_idの_keyを列挙
	cf. keys = keys(map{key})
*/
map_keys{
	_id = _argv[0]
	_keys = IARRAY
	_size = EVAL('ARRAYSIZE(map__keys_' + map__id_num + ')')
	for _i = 0; _i < _size; _i ++ {
		_key = EVAL('map__keys_' + map__id_num + '[_i]')
		if _key != IVOID {
			_keys = (_keys, _key)
		}
	}
	_keys
}

/*
	_junk = map_destroy(_map_id)
	連想配列_map_idを削除
*/
map_destroy{
	_id = _argv[0]
	ERASEVAR('map__keys_' + map__id_num)
	ERASEVAR('map__values_' + map__id_num)
}

map__get_only_index{ // 重複しないようインデックスをずらす
	_id = _argv[0]
	_key = _argv[1]
	_hash = map__hash(_key)
	while (EVAL('map__keys_' + _id + '[_hash]') != IVOID) && (EVAL('map__keys_' + _id + '[_hash]') != _key) {
		if _hash >= (map__hash_size - 1){
			_hash = 0
		} else {
			_hash++
		}
	}
	_hash
}

map__hash{ // ハッシュ関数
	_key = _argv[0]
	_key_len = STRLEN(_key)
	_hash = 0
	for _i = 1; _i < _key_len; _i++ {
		_hash = (_hash * 137 + CHRCODE(_key, _i)) % map__hash_size
	}
	_hash
}

ハッシュ:引数渡しによる実装(おすすめ?)

巨大な引数渡しをする代わりに、自然なロジックと自然な変数のスコープを実現できる。

利点

  • 自然なロジックで実装できる
  • ちゃんと連想配列がローカル変数である

欠点

  • 巨大な引数渡しが遅そう(実用上そう気になるレベルではない気がする)
  • 操作が返り値を使うなどやや奇妙

使用例

// map__hash_size = 2048 // 一連想配列に確保するハッシュ領域のサイズ 実行中一度決めたら変更しない
_m = map_new() // 初期化して実体的な連想配列を取得
_m = map_set(_m, 'key', 'value') // 操作後の連想配列は返り値
_k = map_get(_m, 'key')
_ks = map_keys(_m)
_m = map_delete(_m, 'key')
// 変数の開放は特に必要ない

実装

/*
	_map = map_new()
	連想配列初期化
	cf. map = {}
*/
map_new{
	if ! ISVAR('map__hash_size') {
		map__hash_size = 2048
	}
	IARRAY
}

/*
	_map = map_set(_map, _key, _value)
	連想配列_mapの_keyに_valueをセットした連想配列を返す
	cf. map{key} = value
*/
map_set{
	_key = _argv[_argc - 2]
	_value = _argv[_argc - 1]
	_argv[_argc - 2, _argc - 1] = IARRAY
	_hash = map__get_only_index(_argv, _key)
	_argv[_hash * 2] = _key
	_argv[_hash * 2 + 1] = _value
	_argv
}

/*
	_value = map_get(_map, _key)
	連想配列_mapの_keyを参照
	cf. value = map{key}
*/
map_get{
	_key = _argv[_argc - 1]
	_argv[_argc - 1] = IARRAY
	_hash = map__get_only_index(_argv, _key)
	_argv[_hash * 2 + 1]
}

/*
	_map = map_delete(_map, _key)
	連想配列_mapの_keyを削除した連想配列を返す
	cf. delete(map{key})
*/
map_delete{
	_key = _argv[_argc - 1]
	_argv[_argc - 1] = IARRAY
	_hash = map__get_only_index(_argv, _key)
	_argv[_hash * 2] = IVOID
	_argv[_hash * 2 + 1] = _IVOID
	_argv
}

/*
	_keys = map_keys(_map, _key)
	連想配列_mapの_keyを列挙
	cf. keys = keys(map{key})
*/
map_keys{
	_keys = IARRAY
	for _i = 0; _i < _argc / 2; _i ++ {
		_key = _argv[_i * 2]
		if _key != IVOID {
			_keys = (_keys, _key)
		}
	}
	_keys
}

map__get_only_index{ // 重複しないようインデックスをずらす
	_key = _argv[_argc - 1]
	_argv[_argc - 1] = IARRAY
	_hash = map__hash(_key)
	while (_argv[_hash * 2] != IVOID) && (_argv[_hash * 2] != _key) {
		if _hash >= (map__hash_size - 1){
			_hash = 0
		} else {
			_hash++
		}
	}
	_hash
}

map__hash{ // ハッシュ関数
	_key = _argv[0]
	_key_len = STRLEN(_key)
	_hash = 0
	for _i = 1; _i < _key_len; _i++ {
		_hash = (_hash * 137 + CHRCODE(_key, _i)) % map__hash_size
	}
	_hash
}

トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2014-06-18 (水) 03:35:19