コンテンツにスキップ

ハッシュと値の入れ子(Tc600-3以降)

Tc600-3 以降では、値の型にハッシュ(連想配列)が加わった。また、配列やハッシュの要素に配列やハッシュを入れる(入れ子)ことと、要素の要素へ直接代入する(多次元代入)ことができるようになった。

500 系からの互換性に関わる変更は 600 での変更点 を参照。

ハッシュ

生成

ハッシュは IHASH で作る。キーと値を交互に並べる。IHASH() は空のハッシュになる。

_h = IHASH("name", "さくら", "age", 16)
_e = IHASH()

文字列から作るときは HASH_SPLIT を使う。

_h = HASH_SPLIT("a=1,b=2", ",", "=")

ハッシュの変数は、IHASH などで最初にハッシュとして作っておく必要がある。未定義の変数に _x["a"] = 1 と書いても、ハッシュではなく汎用配列の扱いになる("a" は数値として 0 番目と解釈される)。

要素の参照・代入

h[キー] で値を読み、h[キー] = 値 で追加・上書きする。

_h = IHASH()
_h["name"] = "さくら"
_h["name"]       // "さくら"
_h["none"]       // "" (キーが無いときは空文字列)
_h["age"] += 1   // 複合代入も使える
  • キーが無いときは空文字列を返す。VOID ではない
  • 値には配列やハッシュも入れられる(入れ子)
  • 序数の2番目以降(範囲指定やデリミタ)は無視される。範囲つきの読み出し h[a, b] は空文字列になる

キーの一致

スカラーのキーは「数値」と「文字列」に分けて比べる。

  • 数値として扱うもの: 整数、実数、正規形の整数を表す文字列("9"、"-3" など)
    • "09"、"-0"、"+1"、" 1"、64bit に収まらない数字列は文字列として扱う
  • 数値同士は値で比べる。整数と実数も誤差なく比べる
  • 文字列同士は文字列として比べる。VOID は空文字列と同じキーになる

このため、h[1]、h["1"]、h[1.0] はすべて同じキーになる。一方、h[1.5] と h["1.5"]、h[0] と h["-0"] は別のキーになる。

同じキーとみなされる値を複数回 IHASH に渡したときは、最初のキーと値が残る。

IHASH(9, "i9", "9", "s9")   // 9=i9 だけ

並び順

ハッシュの要素はキーの順に並ぶ。挿入した順ではない。HASH_KEYS、HASH_VALUES、文字列化、foreach、セーブファイルの並びはすべてこの順になる。

  • 数値 < 文字列 の順に並ぶ。数値同士は値の順、文字列同士は文字列の順
  • キーが配列やハッシュのときは、スカラー < 配列 < ハッシュ の順に並び、配列同士・ハッシュ同士は先頭の要素から順に比べる
_h = IHASH(10,1, 9,1, 100,1, -1,1, "b",1, "B",1, 2.5,1)
HASH_KEYS(_h)   // -1,2.5,9,10,100,B,b

値としての振る舞い

操作 結果
真偽 要素が1つ以上あれば真、空なら偽
GETTYPE / GETTYPEEX 5
文字列化(TOSTR や文字列との連結) キー=値 を , でつないだもの。例 a=1,b=x
数値化 0
h1 == h2 要素数、キー、値がすべて一致すれば真。値が入れ子なら中身まで比べる
ハッシュとハッシュ以外の == 常に偽(文字列化した結果が同じでも偽)
+(相手がスカラーかハッシュ) 両方を文字列にして連結する
+(相手が配列) 配列の要素ごとに、ハッシュと文字列として連結する
- * / % 警告 W0024 を出す(下記)
_h = IHASH("a", 1)
_h + 1        // "a=11"
'z' + _h      // "za=1"
_h + _h       // "a=1a=1"
(1,2) + _h    // ("1a=1","2a=1")

- * / %(複合代入を含む)の両辺のどちらかがハッシュのときは、警告 W0024 を出す。結果は、相手がスカラーなら右辺がそのまま返り(h - 1 は 1、1 * h は h)、相手が配列なら要素ごとに計算される。意味のある結果にはならないので使わないこと。なお、配列の要素がハッシュの場合((h, 1) - 1)は W0024 は出ない。

関数の引数と , 演算子

汎用配列は引数や , 演算子で展開されるが、ハッシュは展開されず、そのまま1つの要素になる。

_a = (0, _h, 2)   // 要素数 3。_a[1] はハッシュ
func(_h)          // func の中で _argv[0] がハッシュになる

foreach

ハッシュも foreach で回せる。1変数の形では、値がキーの順に入る。

_h = IHASH("a", 1, "b", 2)
foreach _h ; _v
{
  // _v = 1, 2
}

変数を2つ書くと、1つ目にキー、2つ目に値が入る。この形は配列と簡易配列でも使え、1つ目には 0 から始まるインデックスが入る。

foreach _h ; _k, _v { _s += _k + '=' + _v + ' ' }   // "a=1 b=2 "
foreach ("p", "q") ; _i, _v { ... }                  // _i = 0, 1
foreach "x,y" ; _i, _v { ... }                       // 簡易配列も同じ

詳細は フロー制御 を参照。

値の入れ子

配列の要素、ハッシュの値、ハッシュのキーに、配列やハッシュを入れられる。

_h = IHASH()
_h["list"] = (1, 2, 3)        // ハッシュの値に配列
_h["sub"] = IHASH("x", 1)     // ハッシュの値にハッシュ
_a = (0, _h, 2)               // 配列の要素にハッシュ
_a[1] = _h                    // 同上
_vals = HASH_VALUES(_h)       // 配列の中に配列ができる

読み出しは [ ] を続けて書く。

_h["sub"]["x"]    // 1
_h["list"][1]     // 2
_a[1]["sub"]["x"] // 1

汎用配列の中に配列を直接入れる手段は無い。, 演算子も _a[1] = (8,9) も、配列を展開して差し込む(500 系と同じ)。

_a = (1, 2)
_a[1] = (8, 9)    // 1,8,9

値のコピー

代入や引数渡しは値のコピーになる。コピー元とコピー先は互いに影響しない。

_h = IHASH("a", 1)
_h["self"] = _h
_h["self"]["a"] = 2
_h["a"]           // 1 のまま

多次元代入

要素の要素へ直接代入できる。= のほか += ,= などすべての代入演算子で使える。

_h["sub"]["x"] = 5
_h["list"][0] += 10
_a[1]["sub"]["y"] = "new"
  • 途中の要素が文字列なら簡易配列として書き換える。例: _a[0] が "p,q" のとき、_a[0][1] = 'Q' で _a[0] は "p,Q" になる
  • 途中の要素が無いときは、空文字列を簡易配列として書き換えることになる。ハッシュは自動では作られない。例: 空のハッシュに _h["none"]["x"] = 5 とすると、_h["none"] は文字列 "5" になる
  • 代入の根元は変数でなければならない。(式)[0] = 1 などはエラー E0029
  • フィードバック演算子(参照渡し)でも要素の要素を渡せる(Tc600-4以降)。func(&_h["sub"]["x"]) の中で _argv[0] を書き換えると _h["sub"]["x"] が変わる。Tc600-3 では手前の次元が無視され、_h["x"] に書き込まれていた

制限

  • 文字列化(TOSTR、LOGGING など)した入れ子は、内側もそのまま , や = で展開されるので区切りがあいまいになる。元に戻せる形が必要なときはセーブ形式(SAVEVAR)を使う
  • キーに配列やハッシュを持つハッシュは作れるが、h[キー] の序数の第1要素がキーになるため、配列をキーにして引くことはできない

関連