Lua 常见陷阱

真值定义

问题

在Lua中除了 falsenil 以外的所有值都是真。所以 0 和空字符串 '' 都是真,这和其他语言如 C 、 C++ 有区别。

以下代码打印出 0 is ture.

local v = 0
if v then
    print(v..' is ture')
end

解决方案

多错几次就记住了。

顺便说一句,正因为如此,当 v1 不为 falsenil 时, Lua 表达式 cond and v1 or v2 等效于 C 语言的 cond ? v1 : v2 表达式。

不等号

问题

不等号应该写成 ~= 而不是像大多数语言一样用 !=

解决方案

问题不大,Lua VM 不认识 != 因此是可以检查到的。

未定义的变量当作全局变量

问题

在 Lua 中未定义的变量将被当作全局变量,而且完全合法。

local type = ture
if typo then
    do_some_thing()
end

比如上面的代码由于错误地将 type 写成了 typotypo 被认为是一个全局变量。由于 typo 全局变量没有被赋值。所以为 nil 。因此将不会像想得那样调用 do_some_thing()

解决方案

  1. 使用luacheck这样的静态检查工具,也可配合 Sublime Text 的 SublimeLinter-luacheck 插件或者 Atom 的 linter-luacheck 插件。
  2. 在 Lua 发行包下的 etc 目录有一个 strict.lua 可以用于运行时检查未定义的变量。
  3. 任何变量应该先定义再使用。
  4. 使用可以高亮所有选中变量的编辑器,帮助检查拼写错误。

长度操作符和有洞的表

问题

在 Lua 中,长度操作符用 # 表示,比如 #value

在 Lua 5.1 中表的长度定义为:任何整数索引 n 满足 t[n] 不为 nil 而且 t[n+1]nil 那么 n 即为表 t 的长度。因此对于有洞的表,满足的索引值有多个,而长度操作符的值可能是其中任意一个,具体由 VM 的实现决定。

在 Lua 5.2 和 5.3 中:表的长度仅当表为序列(sequence)的时候才有效。有就是表的正整数键的集合为 {1..n} (n > 1),在这种情况下 n 才是表的长度。像下面的表:

{10, 20, nil, 40}

就不是序列,因为其正整数键的集合为 {1, 2, 4} 。

相应的在 Lua 中:

for i,v in ipairs({10, 20, nil, 40}) do
    -- ...

end

也无法保证能遍历所有正整数键元素。

解决方案

要对表使用长度操作符的时候,务必保证表的数组部分没有洞。

如果不能保证:

  • 需要自己记录最大下标的值。
  • 使用 for i=1,n do ... end 来遍历有洞的数组。

数组下标从 1 开始

问题

在 Lua 中,表的数组部分的下标是从 1 开始的,而其它大多数语言都是从 0 开始。

如果写 t[0] = val 那么它将存储在数组的哈希部分。

同时 ipairs() 只是遍历数组部分,也不是从 0 开始。

解决方案

无完善的解决方案,需要适应和注意;尤其是从其它语言移植代码到 Lua 的时候要特别小心。