Lua

Lua中的三目运算符

Posted by Richbabe on June 29, 2018

三目运算

熟悉 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]