在看 skywind3000 大神写的 z.lua 时发现了神秘写法:

os.argv = arg ~= nil and arg or {}
os.path.sep = windows and '\\' or '/'

不需要怎么思考就能知道这个是三目运算。但是在我认知中,Lua 是不支持三目运算符的,定睛一看才发现这个是通过 Lua 布尔运算中的短路特性模拟的三目运算。

Lua 中的短路特性

短路运算的意思是只有在需要的情况下才去检查条件,例如 a and ba = false 时就不需要检查 b 了,或者是 a or ba = true 时也不用检查 b 了。

Lua 中的短路特性与 C 不同点在于,Lua 的布尔操作结果并不只是「真」和「假」。

例如在 C 中:

int a = 4 || 2 // a 的值是 1

而在 Lua 中:

local a = 4 or 2  -- a 的值是 4
local b = 4 and 2 -- a 的值是 2

Lua 将 nilfalse 当作「假」,其余值都视作「真」(包括 0 )。

然而,Lua 的布尔运算返回的是原值,而不是 truefalse 。我推测这与 Lua 将 nilfalse 都当作「假」有关,如果布尔运算返回了 false ,那 nil 的语意就变化了。

nil 是 Lua 特殊的类型,它可能的值只有 nil ,它不在本文讨论范围之中

Lua 三目运算的原理

下面可以通过例子来说明 a and b or c

  • a = true 时,这时 a and b 的值为 a ,由于后续是 or ,所以可以直接是 c
  • a = false 时,这时 a and b 的值为 b ,由于后续是 or所以可以直接是 b注意这里有个坑等下说

Lua 三目运算的陷阱

在前文中提到,当 a = true 时有坑。当 b 为 false 时,后续是 or 所以最后的值是 c 。这就是 Lua 的三目运算模拟方法失效的场景。

由于 Lua 是一门与 table 结合非常深的语言,我们可以想到利用 table 来解决这一问题,因为 table 在布尔运算中只可能是 nil 和 非nil (表是否为空),只要创造出一个不为空的表,就可以避免之前的 b = false 的情况出现了。

所以最后的解决办法就是:

(a and {b} or {c})[1]

由于表 {b}{c} 不可能是空表,所以就不会「掉坑」。

最后说一句,Lua 真是太奇妙了!