三目运算
熟悉 C/C++ 的老司机都知道三目运算 a ? b : c,这种写法替换简单的条件判断语句可以在不增加阅读难度的情况下,使代码尽量保持简洁。
int a, b, ret;
//if-else
if (a > b)
ret = a;
else
ret = b;
//三目运算符
ret = a > b ? a : b;
Lua 中的三目运算
Lua 原生的语义并没有实现三目运算,一般是通过逻辑运算符 and 和 or 来模拟三目运算符的。
Lua 中 and 和 or 都使用”短路求值(short_cur evaluation)”,也就是说,它们只会在需要时才去评估第二个操作数。(《Lua程序设计》)
local a, b, ret;
ret = a > b and a or b
穷举所有可能性:
a > b 时,ret由a决定:
a > b and a –> true
a or b –> a
a <= b时,ret由b决定:
a > b and a –> false
a or b –> b
示例:
local a, b, ret = 2,1;
ret = a > b and a or b
print(ret)
-> 2(a)
local a, b, ret = 1,2;
ret = a > b and a or b
print(ret)
-> 2(b)
Lua中的三目运算符陷阱
按照从特殊到一般的原则:
- 三目运算的一般形式a ? b : c
a = true,结果为b
a = false,结果为c
- 对应Lua中的a and b or c
b = true
a = true
a and b -> true
b or c -> b
a = false
a and b -> false
b or c -> c
b = false
a = true
a and b -> false
b or c -> c
a = false
a and b -> false
b or c -> c
可以看到当b = false是,Lua模拟的a and b or c始终返回c并不能还原三目运算符的原貌。 《Lua程序设计》也建议这种情况使用 if-else 来避免。
一般化的 a and b or c
那么有没有办法可以解决 b = false 失效的问题呢?
C 语言有一道常规的考题:请使用宏定义写一个返回两个值中较小值的方法。
我们先来看看宏定义:
宏,分为两类:
(1)对象宏(object-like macro) 对于对象宏来说确实相对简单,但却也不是那么简单 的查找替换
(2)函数宏(function-like macro) 函数宏顾名思义,就是行为类似函数;
/*
对象宏
*/
#define M_PI 3.14159265358979323846264338327950288
/*
函数宏
*/
#define PLUS(x,y) x + y
接着我们来看看如何正确使用三目运算的宏定义:
// 正确的认识宏 (三目运算的宏定义)
// (1)小白写法
#define MIN(A,B) A < B ? A : B
int a = MIN(1,2);
// => int a = 1 < 2 ? 1 : 2;
printf("%d",a);
// => 1
// 问题
int a = 2 * MIN(3, 4);
// => int a = 2 * 3 < 4 ? 3 : 4;
// => int a = 6 < 4 ? 3 : 4;
// => int a = 4;
//(2)码农写法
#define MIN(A,B) (A < B ? A : B)
//完美解决
int a = 2 * MIN(3, 4);
// 问题
int a = MIN(3, 4 < 5 ? 4 : 5);
// => int a = (3 < 4 < 5 ? 4 : 5 ? 3 : 4 < 5 ? 4 : 5); //希望你还记得运算符优先级
// => int a = ((3 < (4 < 5 ? 4 : 5) ? 3 : 4) < 5 ? 4 : 5); //为了您不太纠结,我给这个式子加上了括号
// => int a = ((3 < 4 ? 3 : 4) < 5 ? 4 : 5)
// => int a = (3 < 5 ? 4 : 5)
// => int a = 4
// (3)工程师写法
#define MIN(A,B) ((A) < (B) ? (A) : (B))
// 完美解决
int a = MIN(3, 4 < 5 ? 4 : 5);
// 问题
float a = 1.0f;
float b = MIN(a++, 1.5f);
// => float b = ((a++) < (1.5f) ? (a++) : (1.5f))
// (4)大牛写法
#define MIN(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
从这个示例中,我就在想如何能保证 a and b or c 中 b 为真或者 b 不产生歧义呢?
- and的运算优先级高于or,简单的改变运算顺序并没有用
-
这时就想到了lua中万能的table,能不能把a,b,c都放到table中来改变b的存在呢?要注意{nil}也是一个为true的对象。
a,b,c都替换为table:{a} and {b} or {c}
- 三目运算中a是条件,结果是b或者c。其实a并不需要放入table中,否则{a}就始终为true了,失去了条件的意义。而{b} or {c}的结果也必然是一个table,该table只有一个元素。那么通过[1]即可访问
综上所述,更一般化的Lua三目运算为:
(a and {b} or {c})[1]