Lua 运算符

Lua 运算符

Lua提供了丰富的运算符,包括算术运算符、关系运算符、逻辑运算符、字符串运算符、位运算符(Lua 5.3+)等。所有运算符都有明确的优先级规则。

一、算术运算符

1. 基本算术运算符

运算符 描述 示例 结果
+ 加法 3 + 2 5
- 减法 5 - 2 3
* 乘法 3 * 4 12
/ 除法 10 / 2 5.0
% 取模 10 % 3 1
^ 幂运算 2 ^ 3 8
- 负号(一元) -5 -5
lua 复制代码
-- 算术运算符示例
local a, b = 10, 3

print("加法:", a + b)           -- 13
print("减法:", a - b)           -- 7
print("乘法:", a * b)           -- 30
print("除法:", a / b)           -- 3.3333333333333
print("取模:", a % b)           -- 1
print("幂运算:", a ^ 2)         -- 100
print("负号:", -a)              -- -10

-- 注意:除法总是返回浮点数
print(type(10 / 2))             -- number (实际上是 5.0)

-- 取模运算的细节
print("10 % 3 =", 10 % 3)       -- 1
print("-10 % 3 =", -10 % 3)     -- 2 (注意:结果符号与除数相同)
print("10 % -3 =", 10 % -3)     -- -2
print("-10 % -3 =", -10 % -3)   -- -1

-- 取模运算的特性:a % b == a - math.floor(a/b)*b

2. 算术运算的自动类型转换

lua 复制代码
-- 字符串在算术运算中会自动转换为数字
print("10" + 5)        -- 15
print("3.14" * 2)      -- 6.28
print(10 + "5")        -- 15

-- 无法转换时返回nil
local result = "hello" + 5
print(result)          -- nil
print(type(result))    -- nil

二、关系运算符

1. 基本关系运算符

运算符 描述 示例 结果
== 等于 5 == 5 true
~= 不等于 5 ~= 3 true
< 小于 3 < 5 true
> 大于 5 > 3 true
<= 小于等于 5 <= 5 true
>= 大于等于 5 >= 5 true
lua 复制代码
-- 关系运算符示例
local a, b = 10, 5

print("相等:", a == b)      -- false
print("不等:", a ~= b)      -- true
print("小于:", a < b)       -- false
print("大于:", a > b)       -- true
print("小于等于:", a <= b)  -- false
print("大于等于:", a >= b)  -- true

2. 比较运算的特殊规则

lua 复制代码
-- 规则1:不同类型的值不相等
print(1 == "1")        -- false
print(0 == false)      -- false
print(nil == false)    -- false

-- 规则2:table和userdata比较的是引用,不是内容
local t1 = {1, 2, 3}
local t2 = {1, 2, 3}
local t3 = t1

print(t1 == t2)        -- false (不同的引用)
print(t1 == t3)        -- true (相同的引用)

-- 规则3:nil只等于nil
print(nil == nil)      -- true

3. 浮点数比较的陷阱和解决方案

lua 复制代码
-- 浮点数精度问题
local a = 0.1 + 0.2
local b = 0.3

print("直接比较:", a == b)  -- false

-- 解决方案1:使用误差范围
local epsilon = 1e-10
function float_equal(x, y)
    return math.abs(x - y) < epsilon
end

print("使用误差范围:", float_equal(a, b))  -- true

-- 解决方案2:使用math.abs
local threshold = 0.0000001
print("使用math.abs:", math.abs(a - b) < threshold)  -- true

-- 解决方案3:转为字符串格式化比较(适用于特定精度需求)
function float_equal_format(x, y, precision)
    precision = precision or 6
    return string.format("%." .. precision .. "f", x) == 
           string.format("%." .. precision .. "f", y)
end

print("格式化比较:", float_equal_format(a, b, 10))  -- true

三、逻辑运算符

1. 基本逻辑运算符

运算符 描述 示例 结果
and 逻辑与 true and false false
or 逻辑或 true or false true
not 逻辑非 not true false
lua 复制代码
-- 逻辑运算符示例
print("and 运算:")
print(true and true)     -- true
print(true and false)    -- false
print(false and true)    -- false
print(false and false)   -- false

print("\nor 运算:")
print(true or true)      -- true
print(true or false)     -- true
print(false or true)     -- true
print(false or false)    -- false

print("\nnot 运算:")
print(not true)          -- false
print(not false)         -- true
print(not nil)           -- true (nil视为false)
print(not 0)             -- false (0视为true)

2. 逻辑运算符的短路求值

lua 复制代码
-- and: 如果第一个操作数为假,直接返回第一个操作数,否则返回第二个操作数
print(1 and 2)           -- 2
print(nil and 2)         -- nil
print(false and error("不会执行"))  -- false (不会触发error)

-- or: 如果第一个操作数为真,直接返回第一个操作数,否则返回第二个操作数
print(1 or 2)            -- 1
print(nil or 2)          -- 2
print(true or error("不会执行"))   -- true (不会触发error)

-- 实用技巧:提供默认值
local config = {}
local value = config.port or 8080
print("端口:", value)    -- 8080

-- 实用技巧:条件赋值
local x = nil
local y = x or 100
print("y =", y)          -- 100

-- 实用技巧:简化if语句
function print_if_positive(n)
    (n > 0) and print("正数:", n)
end

print_if_positive(5)     -- 输出: 正数: 5
print_if_positive(-3)    -- 无输出

3. 三元运算符模拟

lua 复制代码
-- Lua没有三元运算符,但可以用and/or模拟
-- condition ? value_if_true : value_if_false
-- 在Lua中: (condition and value_if_true) or value_if_false

local age = 18
local status = (age >= 18 and "成年") or "未成年"
print("状态:", status)  -- 成年

-- 注意:当value_if_true为false或nil时,这种方法会失效
local flag = false
local result = (flag and "true") or "false"
print("结果:", result)  -- false (正确,因为flag为false)

-- 但当value_if_true本身可能是false时
local show = false
local display = (show and false) or true
print("显示:", display)  -- true (错误!应该为false)
-- 这是因为: false and false 返回 false, false or true 返回 true

-- 解决方案:使用显式if语句或函数
function ternary(condition, true_val, false_val)
    if condition then
        return true_val
    else
        return false_val
    end
end

local display2 = ternary(show, false, true)
print("显示(正确):", display2)  -- false

四、字符串运算符

1. 字符串连接运算符

运算符 描述 示例 结果
.. 字符串连接 "Hello" .. "World" "HelloWorld"
lua 复制代码
-- 字符串连接
local s1 = "Hello"
local s2 = "World"
local s3 = s1 .. " " .. s2
print(s3)  -- Hello World

-- 数字会自动转换为字符串
local num = 42
print("答案是: " .. num)  -- 答案是: 42

-- 连接大量字符串时注意效率
-- 低效的方式(每次连接都创建新字符串)
local result = ""
for i = 1, 1000 do
    result = result .. i  -- 每次循环都创建新字符串
end

-- 高效的方式(使用table.concat)
local parts = {}
for i = 1, 1000 do
    parts[i] = tostring(i)
end
local result2 = table.concat(parts)

2. 字符串长度运算符

运算符 描述 示例 结果
# 字符串长度 #"Hello" 5
lua 复制代码
-- 字符串长度
print(#"Hello")          -- 5
print(#"")               -- 0
print(#"中文")           -- 2 (注意:计算的是字符数,不是字节数)

-- 多字节字符处理
local str = "Hello世界"
print("字符串长度:", #str)  -- 7 (5个英文字母 + 2个中文字符)

五、位运算符(Lua 5.3+)

Lua 5.3 引入了对位运算的支持,所有位运算符都只对整数有效。

运算符 描述 示例 结果
& 按位与 5 & 3 1
` ` 按位或 `5
~ 按位异或 5 ~ 3 6
>> 右移 8 >> 1 4
<< 左移 1 << 3 8
~ 按位非(一元) ~5 -6
lua 复制代码
-- 位运算符示例
local a, b = 5, 3  -- 二进制: 101, 011

print("按位与:", a & b)      -- 1 (二进制: 001)
print("按位或:", a | b)      -- 7 (二进制: 111)
print("按位异或:", a ~ b)    -- 6 (二进制: 110)
print("左移:", a << 1)       -- 10 (二进制: 1010)
print("右移:", a >> 1)       -- 2 (二进制: 010)
print("按位非:", ~a)         -- -6 (二进制补码表示)

-- 位运算实用技巧
-- 1. 检查奇偶性
function is_odd(n)
    return (n & 1) == 1
end

print("5是奇数吗?", is_odd(5))  -- true
print("4是奇数吗?", is_odd(4))  -- false

-- 2. 乘除2的幂次
local x = 10
print("乘以8:", x << 3)    -- 80 (10 * 2^3)
print("除以4:", x >> 2)    -- 2 (10 / 2^2)

-- 3. 交换两个数(不使用临时变量)
a, b = 5, 10
a = a ~ b
b = a ~ b
a = a ~ b
print("交换后:", a, b)     -- 10, 5

-- 4. 判断是否为2的幂
function is_power_of_two(n)
    return n > 0 and (n & (n - 1)) == 0
end

print("8是2的幂吗?", is_power_of_two(8))   -- true
print("7是2的幂吗?", is_power_of_two(7))   -- false

六、其他运算符

1. 长度运算符(用于表)

lua 复制代码
-- 长度运算符#用于获取表的数组部分的长度
local arr = {1, 2, 3, 4, 5}
print("数组长度:", #arr)  -- 5

-- 注意:只计算连续整数索引部分
local t = {1, 2, 3}
t[5] = 5
print("非连续数组长度:", #t)  -- 3 (因为t[4]是nil)

-- 对于有空洞的数组,行为是未定义的
local sparse = {1, nil, 3}
print("稀疏数组长度:", #sparse)  -- 可能是nil或1或3,取决于实现

-- 安全获取表长度的方法
function safe_length(t)
    local count = 0
    for _ in pairs(t) do
        count = count + 1
    end
    return count
end

print("安全长度:", safe_length(sparse))  -- 2

2. 索引运算符

lua 复制代码
-- 表索引
local t = {name = "Lua", version = 5.4}

-- 点语法(字符串键)
print(t.name)      -- Lua
print(t.version)   -- 5.4

-- 方括号语法
print(t["name"])   -- Lua

-- 动态键名
local key = "version"
print(t[key])      -- 5.4

-- 方法调用(实际上是索引+调用)
local str = "hello"
print(str:upper())  -- HELLO (等价于 string.upper(str))

七、运算符优先级

Lua运算符的优先级从高到低如下:

优先级 运算符 描述
1 ^ 幂运算
2 not # - (一元) 逻辑非、长度、负号
3 * / % 乘、除、取模
4 + - 加、减
5 .. 字符串连接
6 << >> 位左移、右移
7 & 按位与
8 ~ 按位异或
9 ` `
10 < > <= >= ~= == 关系运算符
11 and 逻辑与
12 or 逻辑或
lua 复制代码
-- 优先级示例
local result = 1 + 2 * 3 ^ 2
print(result)  -- 19 (计算顺序: 3^2=9, 2*9=18, 1+18=19)

result = not true and false or true
print(result)  -- true (计算顺序: not true=false, false and false=false, false or true=true)

-- 使用括号改变优先级
result = (1 + 2) * 3 ^ 2
print(result)  -- 27 (计算顺序: 1+2=3, 3^2=9, 3*9=27)

-- 字符串连接的优先级
local str = "a" .. "b" == "ab"
print(str)  -- true (注意: ..的优先级高于==)

-- 容易出错的优先级
local a, b, c = 1, 2, 3
local x = a + b * c  -- 7 (不是9)
local y = (a + b) * c  -- 9
print("x =", x, "y =", y)

八、特殊运算符和语法糖

1. 多重赋值运算符

lua 复制代码
-- 多重赋值
local a, b, c = 1, 2, 3
print(a, b, c)  -- 1, 2, 3

-- 变量数量多于值数量
local x, y, z = 10, 20
print(x, y, z)  -- 10, 20, nil

-- 值数量多于变量数量
local m, n = 1, 2, 3, 4
print(m, n)     -- 1, 2 (多余的值被忽略)

-- 交换变量值
a, b = b, a
print("交换后:", a, b)  -- 2, 1

-- 函数返回多个值
function get_values()
    return 10, 20, 30
end

local p, q, r = get_values()
print(p, q, r)  -- 10, 20, 30

2. 复合赋值运算符

Lua不支持C语言风格的复合赋值运算符(如+=、-=等),但可以模拟:

lua 复制代码
-- Lua没有 += 运算符
local count = 0
count = count + 1  -- 正确的写法
-- count += 1      -- 错误的写法,Lua不支持

-- 模拟复合赋值
function add_assign(t, k, v)
    t[k] = (t[k] or 0) + v
end

local stats = {score = 100}
add_assign(stats, "score", 50)
print("分数:", stats.score)  -- 150

-- 使用metatable模拟(高级技巧)
local mt = {
    __add = function(t, v)
        local result = {}
        for k, val in pairs(t) do
            result[k] = val + v
        end
        return result
    end
}

local vec = {x = 1, y = 2}
setmetatable(vec, mt)
vec = vec + 3
print("向量:", vec.x, vec.y)  -- 4, 5

九、运算符重载(通过元表)

Lua允许通过元表重载某些运算符:

lua 复制代码
-- 定义向量类型
local Vector = {}

function Vector.new(x, y)
    local v = {x = x or 0, y = y or 0}
    setmetatable(v, Vector.mt)
    return v
end

Vector.mt = {
    -- 加法重载
    __add = function(a, b)
        return Vector.new(a.x + b.x, a.y + b.y)
    end,
    
    -- 减法重载
    __sub = function(a, b)
        return Vector.new(a.x - b.x, a.y - b.y)
    end,
    
    -- 乘法重载(标量乘法)
    __mul = function(a, scalar)
        if type(scalar) == "number" then
            return Vector.new(a.x * scalar, a.y * scalar)
        else
            error("只能与标量相乘")
        end
    end,
    
    -- 相等判断重载
    __eq = function(a, b)
        return a.x == b.x and a.y == b.y
    end,
    
    -- 字符串表示重载
    __tostring = function(v)
        return string.format("Vector(%f, %f)", v.x, v.y)
    end,
    
    -- 长度运算符重载
    __len = function(v)
        return math.sqrt(v.x * v.x + v.y * v.y)
    end
}

Vector.__index = Vector

-- 使用重载的运算符
local v1 = Vector.new(1, 2)
local v2 = Vector.new(3, 4)

local v3 = v1 + v2
print("向量加法:", v3)  -- Vector(4.000000, 6.000000)

local v4 = v2 - v1
print("向量减法:", v4)  -- Vector(2.000000, 2.000000)

local v5 = v1 * 3
print("标量乘法:", v5)  -- Vector(3.000000, 6.000000)

print("向量相等?", v1 == v2)  -- false

print("向量长度:", #v1)  -- 2.2360679774998 (√(1²+2²))

十、运算符使用的最佳实践

  1. 使用括号提高可读性:即使知道优先级,使用括号可以使意图更清晰
  2. 注意浮点数比较:永远不要直接比较浮点数是否相等
  3. 利用短路求值:简化条件判断和提供默认值
  4. 字符串连接性能:大量字符串连接时使用table.concat
  5. 理解nil的行为:nil在逻辑运算中的特殊行为
  6. 类型一致性:确保比较运算中的类型一致
  7. 位运算符限制:位运算符只适用于整数(Lua 5.3+)
  8. 表长度陷阱:#运算符只适用于连续数组部分
lua 复制代码
-- 良好的实践示例

-- 1. 使用括号明确优先级
local result = (a + b) * (c - d)  -- 比 a + b * c - d 更清晰

-- 2. 安全的浮点数比较
function float_equal(a, b, epsilon)
    epsilon = epsilon or 1e-10
    return math.abs(a - b) < epsilon
end

-- 3. 使用and/or提供默认值
local port = config.port or 8080
local host = config.host or "localhost"

-- 4. 高效字符串构建
function build_string(parts)
    return table.concat(parts, ", ")
end

-- 5. 安全获取表长度
function table_length(t)
    local count = 0
    for _ in pairs(t) do
        count = count + 1
    end
    return count
end

-- 6. 类型检查
function safe_add(a, b)
    if type(a) ~= "number" or type(b) ~= "number" then
        return nil, "参数必须是数字"
    end
    return a + b
end

总结

Lua的运算符系统虽然简洁但功能强大,理解每个运算符的行为、优先级和特性对于编写正确、高效的Lua代码至关重要。特别注意Lua特有的行为,如逻辑运算符的短路求值、只有nil和false为假、浮点数比较的精度问题等。

通过元表,Lua还允许对运算符进行重载,这为创建自定义类型和实现运算符多态提供了可能。掌握这些知识可以帮助你更好地利用Lua语言的特性,编写出更优雅、更高效的代码。

相关推荐
Rabbit_QL1 天前
【水印添加工具】从零设计一个工程级 Python 图片水印工具:WaterMask 架构与实现
开发语言·python
天“码”行空1 天前
简化Lambda——方法引用
java·开发语言
z20348315201 天前
C++对象布局
开发语言·c++
Beginner x_u1 天前
如何解释JavaScript 中 this 的值?
开发语言·前端·javascript·this 指针
预立科技1 天前
Redis 中 Lua 与 Pipeline 的相同点,区别,使用场景
redis·pipeline·lua
java1234_小锋1 天前
Java线程之间是如何通信的?
java·开发语言
张张努力变强1 天前
C++ Date日期类的设计与实现全解析
java·开发语言·c++·算法
feifeigo1231 天前
基于EM算法的混合Copula MATLAB实现
开发语言·算法·matlab
LYS_06181 天前
RM赛事C型板九轴IMU解算(4)(卡尔曼滤波)
c语言·开发语言·前端·卡尔曼滤波
盛世宏博北京1 天前
高效环境管控:楼宇机房以太网温湿度精准监测系统方案
开发语言·数据库·php·以太网温湿度变送器