先前我说 Lua 数组从 1 开始不太爽,很多人来纠正我说也可以从 0 开始,比如:
local m = { [0] = 100, 101, 102, 103 }
然后访问时 m[0] 也可以正常访问到第 0 个元素,所以 “Lua 给你充分自由度,让你可以从任意下标索引数组”,貌似好像说的很有道理,但是不是这样呢?
我们先用 #
符号打印下上面数组的长度:
print('size', #m)
输出是:3 ,而不是实际元素个数 4,因为 #
就是从 1 开始数起的,所以如果你代码里用了 m[0] ,你也需要额外方式计算长度,同时保证用到这个数组的其他代码也遵从这样计算。
还有一个问题,使用 ipairs
遍历的时候,m[0] 不会被遍历进去:
for i, j in ipairs(m) doprint(i, '->', j)
end
输出是:
1 -> 101
2 -> 102
3 -> 103
看到没,你的 m[0] 没了,即便你写了个 m[0] = 100 ,再 ipairs
那里也不认,Lua 没把他算在整数索引范围。那么如果你创建一个数组从 0 开始索引的话,你就要通知所有用你数组的人,既不能用 #
也不能用 ipairs
来遍历,这种沟通成本和后续无穷的麻烦,你愿意接受吗?
那么你说,我们不用 ipairs
,改用 pairs
来遍历行不行?行,你可以这么写:
for i, j in pairs(m) doprint(i, '->', j)
end
但数组从 0 开始的话,0 元素没有保存在 array part 里,会导致遍历顺序不一样(因为优先遍历 array part),上面代码的输出是:
1 -> 101
2 -> 102
3 -> 103
0 -> 100
看到没,先遍历的 1-3(他们在 array part 里),最后再遍历 hash part 里 0。你喜欢这样的无序遍历的数组么?还是继续坚持 for i = 0, N-1 do 来自己遍历,并通知你的同事这样才能保持顺序。
最后一个问题是,一个 table 中 1-n 的连续整数索引都会被保存到 array part 里,而其他会被保存到 hash part 里,不管是检索还是遍历,都会优先到 array part 里用 O(1) 的方式检索,不行再到 hash part 用非 O(1) 的方式同其他 key 一起检索,那么你 m[0] 是游离在 array part 外的键,不但遍历顺序靠后,没和其他元素放一起,每次检索还有额外代价。
因此 Lua 支持数组从 0 开始索引么?只能说允许你这么用,但是语言层面并不提供足够的支持。
那么又会有人混淆视听的说:“从 1 开始也挺好的啊,我有着并没用什么问题”,但他们是不是忘记了 Lua 是嵌入式语言,要依靠宿主 C 语言提供运行环境,数组从 1 开始的话,和 C 语言宿主存在一个换算的关系,两边都写得话,一会从 0 一会从 1 ,引入了额外的负担,不留神就 BUG 了。
–
扩展阅读:还有觉得从 1 开始更合理的点这里
为什么 C 语言数组是从 0 开始计数的?