<返回目录     Powered by claud/xia兄

第3课: 运算符与表达式

运算符概述

运算符是编程语言的基础构建块,用于对操作数执行各种运算。Lua提供了丰富的运算符集合,理解这些运算符的内部实现和工作原理对于编写高效、正确的代码至关重要。

Lua运算符的设计哲学:

算术运算符

算术运算符用于执行数学运算。Lua 5.3+引入了整数和浮点数的明确区分,这对数值运算的精度和性能有重要影响。

运算符 说明 示例 结果 版本
+ 加法 10 + 5 15 所有版本
- 减法 10 - 5 5 所有版本
* 乘法 10 * 5 50 所有版本
/ 除法 10 / 4 2.5 所有版本
// 整除 10 // 4 2 5.3+
% 取模 10 % 3 1 所有版本
^ 幂运算 2 ^ 3 8 所有版本
- 负号 -10 -10 所有版本

算术运算的深入理解

数值类型与运算规则:

Lua 5.3+引入了整数和浮点数的明确区分:

-- 整数和浮点数的运算规则
print(10 + 5)      -- 15 (整数)
print(10.0 + 5)    -- 15.0 (浮点数)

-- 除法总是返回浮点数
print(10 / 2)      -- 5.0
print(10 // 2)     -- 5 (整除,返回整数)

-- 整数运算示例
local a = 2147483647  -- 最大32位有符号整数
local b = a + 1
print(b)              -- 2147483648 (自动转换为浮点数)

-- 幂运算总是返回浮点数
print(2 ^ 10)     -- 1024.0
print(4 ^ 0.5)    -- 2.0 (平方根)

-- 取模运算的特殊性质
print(10 % 3)     -- 1
print(-10 % 3)    -- 2 (Lua的取模结果符号与除数相同)
print(10 % -3)    -- -2

浮点数精度问题

浮点数精度注意事项:

由于浮点数使用IEEE 754标准表示,存在精度限制:

-- 浮点数精度示例
print(0.1 + 0.2)           -- 0.30000000000000004
print(0.1 + 0.2 == 0.3)    -- false

-- 正确的浮点数比较方法
function almostEqual(a, b, epsilon)
    epsilon = epsilon or 1e-10
    return math.abs(a - b) < epsilon
end

print(almostEqual(0.1 + 0.2, 0.3))  -- true

-- 特殊浮点数值
print(1.0 / 0.0)       -- inf (正无穷)
print(-1.0 / 0.0)      -- -inf (负无穷)
print(0.0 / 0.0)       -- nan (非数字)
print(math.sqrt(-1))    -- nan

关系运算符

关系运算符用于比较两个值,返回布尔结果。理解关系运算符的类型转换规则对于避免常见错误非常重要。

运算符 说明 示例
== 等于 5 == 5 (true)
~= 不等于 5 ~= 3 (true)
> 大于 5 > 3 (true)
< 小于 5 < 3 (false)
>= 大于等于 5 >= 5 (true)
<= 小于等于 5 <= 3 (false)

关系运算的深入理解

关系运算的类型规则:
 b)    -- false
print(a < b)    -- true
print(a >= 10)  -- true
print(b <= 20)  -- true

-- 整数和浮点数混合比较
print(10 == 10.0)   -- true
print(10 < 10.5)     -- true

-- 字符串比较(字典序)
print("abc" < "xyz")  -- true
print("ABC" < "abc")  -- true (大写字母的ASCII码更小)
print("10" < "2")     -- true (按字典序,不是数值)

-- 布尔值比较
print(false < true)   -- true
print(false == 0)     -- false (不同类型)

-- 表和函数的比较(引用比较)
local t1 = {1, 2, 3}
local t2 = {1, 2, 3}
local t3 = t1
print(t1 == t2)  -- false (不同的表对象)
print(t1 == t3)  -- true (同一个表对象)

-- nil的比较
print(nil == nil)  -- true
print(nil < 1)     -- 错误:不能比较nil和数字

逻辑运算符

逻辑运算符用于布尔逻辑运算,但Lua的逻辑运算符具有特殊的短路求值特性和灵活的返回值规则。

运算符 说明 示例
and 逻辑与 true and false (false)
or 逻辑或 true or false (true)
not 逻辑非 not true (false)

逻辑运算的深入理解

逻辑运算的真值表:

Lua中只有nil和false被视为假值,其他所有值(包括0、空字符串、空表)都是真值:

短路求值

短路求值原理:

逻辑运算符具有短路特性,只在必要时才计算第二个操作数:

这种特性可以用于条件执行和默认值设置。

 5
print(result1)  -- nil (没有计算x > 5)

-- or运算:第一个为真时短路
local result2 = 10 or x > 5
print(result2)  -- 10 (没有计算x > 5)

-- 短路求值的实际应用
-- 防止空指针错误
local t = nil
local value = t and t.value  -- 安全,不会报错
print(value)  -- nil

-- 默认值设置
local name = inputName or "匿名用户"

-- 条件执行
x > 5 and print("x大于5")  -- 相当于if x > 5 then print("x大于5") end

-- 三元运算符的模拟
local max = (a > b) and a or b

逻辑运算的返回值

逻辑运算的返回值规则:

逻辑运算符返回的是操作数的实际值,而不是简单的true/false:

位运算符(Lua 5.3+)

位运算符用于对整数的二进制位进行操作。这些运算符在Lua 5.3中引入,主要用于底层编程和性能优化。

运算符 说明 示例 结果
& 按位与 5 & 3 1
| 按位或 5 | 3 7
~ 按位异或 5 ~ 3 6
>> 右移 8 >> 1 4
<< 左移 4 << 1 8
~ 按位非 ~5 -6
> 1)  -- 4 (右移1位,相当于除以2)
print(4 << 1)  -- 8 (左移1位,相当于乘以2)
print(1 << 3)  -- 8 (左移3位)

-- 按位非(取反)
print(~5)      -- -6 (二进制补码表示)

-- 位运算的实际应用
-- 检查奇偶性
function isEven(n)
    return (n & 1) == 0
end
print(isEven(4))  -- true
print(isEven(5))  -- false

-- 设置标志位
local flags = 0
flags = flags | (1 << 0)  -- 设置第0位
flags = flags | (1 << 2)  -- 设置第2位
print(flags)  -- 5

-- 检查标志位
function hasFlag(flags, bit)
    return (flags & (1 << bit)) ~= 0
end
print(hasFlag(flags, 0))  -- true
print(hasFlag(flags, 1))  -- false

字符串连接运算符

字符串连接使用..运算符。理解字符串连接的内存管理机制对于优化性能很重要。

字符串连接的性能优化

字符串连接的性能考虑:

在循环中连接字符串时,每次连接都会创建新的字符串对象,导致性能问题:

长度运算符

长度运算符#用于获取字符串或表的长度。

表长度的边界定义

表长度的边界规则:

Lua中表的长度定义为"边界索引",即满足t[i]不为nil且t[i+1]为nil的最大整数i:

运算符优先级

运算符优先级决定了表达式中运算的执行顺序。理解优先级规则对于编写正确的表达式至关重要。

从高到低的优先级:

  1. ^ (幂运算,右结合)
  2. not # - (一元运算符)
  3. * / // %
  4. + -
  5. .. (字符串连接)
  6. < > <= >= ~= ==
  7. and
  8. or
 5 and 3 < 4)   -- true
print(10 > 5 or 3 < 4)    -- true (短路)

运算符重载

通过元表机制,Lua支持运算符重载,使得自定义类型可以像内置类型一样使用运算符。

可重载的运算符元方法:

综合示例

 0 and b > 0 and c > 0

    -- 逻辑运算
    local isValid = isPositive and average > 10

    -- 字符串连接
    local result = string.format(
        "和: %d, 积: %d, 平均值: %.2f, 有效: %s",
        sum, product, average, tostring(isValid)
    )

    return result
end

print(calculate(10, 20, 30))

-- 使用运算符重载的复数类
local Complex = {}
Complex.__index = Complex

function Complex.new(real, imag)
    local c = {real = real, imag = imag}
    setmetatable(c, Complex)
    return c
end

function Complex.__add(a, b)
    return Complex.new(a.real + b.real, a.imag + b.imag)
end

function Complex.__mul(a, b)
    return Complex.new(
        a.real * b.real - a.imag * b.imag,
        a.real * b.imag + a.imag * b.real
    )
end

function Complex.__tostring(c)
    if c.imag >= 0 then
        return string.format("%g+%gi", c.real, c.imag)
    else
        return string.format("%g%gi", c.real, c.imag)
    end
end

local c1 = Complex.new(1, 2)
local c2 = Complex.new(3, 4)
print(c1 + c2)  -- 4+6i
print(c1 * c2)  -- -5+10i
重要提示:
练习题:
  1. 计算表达式:(10 + 5) * 2 - 8 / 4,并解释运算顺序
  2. 编写程序判断一个数是否在10到20之间(使用关系和逻辑运算符)
  3. 使用逻辑运算符实现三元运算符效果:max = (a > b) and a or b
  4. 计算字符串"Hello Lua"的长度,并处理UTF-8中文字符
  5. 实现一个复数类,支持加减乘除运算和字符串转换
  6. 使用位运算符实现标志位管理(设置、清除、检查)
  7. 编写一个安全的除法函数,处理除数为0的情况
  8. 实现一个函数,返回多个参数中的第一个非nil值