Luaの長さ演算子

長さ演算子は単項演算子 # で表される。文字列の長さはそのバイト数である (つまり、各文字が1バイトだとした場合の、文字列の長さである)。

テーブル t の長さは、 t[n] が nil でなく t[n+1] が nil であるような整数 n と定義されている。また、t[1] が nil なら n はゼロになりうる。 nil でない値が1から n まで格納されている普通の配列では、その長さは最後のインデックス n を正しく返す。配列に「穴」がある場合 (つまり間に nil がある場合)、 #t はどれかの nil のひとつ前を返すかもしれない (つまり、その nil が配列の終わりであるように見えるのだ)。

2.5.5 - 長さ演算子

つまり、配列にnilを格納する場合は長さ演算子 # を使うと正しい長さを取得できないかも知れないので、自前で長さを管理しなければならないのだろうか?実験してみる。

function main()
   print(#{ 1, 2 })       -- 2
   print(#{ 1, nil })     -- 1
   print(#{ nil, 1 })     -- 2
   print(#{ nil, nil })   -- 0
   print(#{ 1, nil, 3 })  -- 3
   print(#{ 1, 2, nil })  -- 2
   print(#{ nil, 1, nil, 2 }) -- 4
end

main()

実行結果です。(上のコメントと同じものです)

2
1
2
0
3
2
4

この結果だけみると、末尾のnilだけ無視されてしまっているような感じです。

『Programmng in Lua』を読み返してみると、p.35に以下のような記述がありました。

配列に穴がある―途中にnilの要素がある―と、長さ演算子はこれらのnil要素のいずれかを末尾を意味するマーカーとみなしてしまうかもしれません。もちろん、このような予測不能な動作は望むところではありません。したがって、穴を含んでいる可能性のある配列に対しては、長さ演算子はしないようにすべきです。

追記:

さらに読み進めると以下の記述がありました(p.35)。

最後のインデックスに至る途中に穴の開いている配列をどうしても使用したいのであれば、table.maxn関数を使用するとよいでしょう。この関数はテーブルのインデックスで最大の正数を返します。

実験してみます。

function main()
   local tab = {}
   tab[3000] = 1
   print(#tab)
   print(table.maxn(tab))
end

main()

実行結果です。

0
3000