読者です 読者をやめる 読者になる 読者になる

Minecraftとタートルと僕

PCゲームMinecraftのMOD「ComputerCraft」の情報を集めたニッチなブログです。

Luaが配列(Array)を実装しないのって何で?教えてエロイ人

はじめに

今回はComputerCraftから離れて、Luaの言語仕様について愚痴みたいなものを語っちゃおう。

Luaはシンプルなプログラミング言語なのでその仕様の簡潔さと割切り具合については驚くべきものがあるんだ。なかでも僕が一番驚いたのは、「配列は実装されて無いからTableという名の連想配列を使ってね」というところ。
でもぶっちゃけ、配列(Array)と連想配列(Hash)を分けないのって混乱の元じゃない?


とりあえず仕様の解説

以下、Windowsでの代表的なLua実装であるluaforwindowsを使って実例を示そう。
Luaインタプリンタを使ってみんなも試してみてね。

# テーブルは連想配列(Hash)だよ
> my_table = {a="hoge", b="hogehoge"}

# 中身を見ると
> print(my_table["a"])
hoge
> print(my_table["b"])
hogehoge
# テーブルは配列(Array)としても使えるよ
> my_table = {"hoge", "hogehoge"}

# 中身を見ると
> print(my_table[1])
hoge
> print(my_table[2])
hogehoge

おやおや?なんだか変だよ

ではここでこういうことをやってみよう。

> hen1 = {}
> hen1[1] = 'hoge'
> hen1[2] = 'hogehogehoge'
> hen1['a'] = 'abcde'
> hen1[3] = 'endend'

さて問題です。Tableである hen1 に対して
「#table」のような要素の個数を返す標準メソッドを使うとどんな挙動をするかな?

# 要素の個数を調べるとこんな感じに・・・え?
> print(#hen1)
2
# Tableの中身はこうなっているよ
> for key,value in pairs(hen1) do
>>    print(key, ' = ', value)
>>  end
1 = hoge
2 = hogehoge
a = abcde
3 = endend

結局どうなっているの?

調べてみるとLuaのTable型は、以下のような性質を持っていることがわかったよ

  • Table型は、内部に「Array」保存部分と「Hash」保存部分を持っている
  • 「table[key] = X」の形で代入を繰り返すときに、keyが1から順番に増えていく数字ならそれはArray部分に保存していくよ
  • でも、keyが順番ではなくいきなり大きく飛んだり、あるいは文字列だったらそれ以降は、Hash部分に保存するよ。それ以降どんなkeyを使おうがあとはもうHashとみなすので。もう不可逆。
  • Table型のメソッド「#」や「table.len()」は、Table型のArray部分に保存されたものだけに適用されるよ。また「table.sort()」とかに代表される並び順に関してもArray部分でしか認識されない。
  • Hash保存部分は長さとか認識されないし、並び順も関係ないよ。
  • pairs と ipairs は区別して使ってね
-- pairs はArray部とHash部を区別せずにTable全体を取り出すよ
for k,v in pairs(t) do
  処理部
end

-- ipairs はArray部だけを取り出すよ
for k,v in ipairs(t) do
  処理部
end

結論

間違い「Luaは配列を実装してなくてTableという名の連想配列を使って表現しているよ」
正解「LuaのTableは、Array型+Hash型 という混合型なんだよ!!!」

きもちわるいです・・・

さてそれでは以下の場合どんな挙動するか。Luaインタプリンタで試してみて? 背筋のぞわぞわする気持ち悪さを感じるといいと思うよ。

# まあ仕様上そうなるよね という例
> t = {}
> t[1] = 1
> t[2] = 2
> t[5] = 5

> print(#t)
# ちょっwおまっw ってなる例
> t = {}
> t[1] = 1
> t[2] = 2
> t[4] = 4

> print(#t)
# なんぞこれ・・・
> t = {'a','b','c',A='hoge','d'}

> print(#t)

> for key,value in pairs(t) do
>>    print(key, ' = ', value)
>>  end