Lua 脚本

Lua 是一门由巴西里约热内卢天主教大学(PUC-Rio)的 Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo 于 1993 年开发的轻量级、高效、可嵌入的脚本语言。它的设计目标是成为一门 "胶水语言",能够轻松嵌入到其他应用程序中,提供灵活的扩展能力。

经过 30 多年的发展,Lua 已经成为全球最流行的嵌入式脚本语言之一,广泛应用于游戏开发(Roblox、魔兽世界、Unity)、Web 开发(OpenResty)、数据库(Redis、Tarantool)、编辑器扩展(Neovim)以及嵌入式设备等领域。Lua 的核心优势在于其极小的体积(编译后仅几百 KB)、极快的执行速度(基于寄存器的虚拟机)、卓越的可移植性以及简单优雅的语法。

本文将系统讲解 Lua 脚本的核心知识,从基础语法到高级特性,再到企业级实战应用,帮助你全面掌握这门强大的语言。

一、Lua 基础语法与核心特性

Lua 是一门动态类型、过程式、函数式的语言,语法简洁优雅,学习曲线平缓。

1.1 变量与数据类型

Lua 有 8 种基本数据类型:nilbooleannumberstringfunctionuserdatathreadtable

(1)变量声明

Lua 中的变量默认是全局变量,除非用 local 关键字显式声明为局部变量。强烈建议尽量使用局部变量,因为局部变量的访问速度比全局变量快得多,而且可以避免命名空间污染。

Lua 复制代码
-- 全局变量(不推荐)
global_var = "I am global"

-- 局部变量(推荐)
local local_var = "I am local"

-- 同时声明多个局部变量
local a, b, c = 1, 2, 3

(2)基本数据类型详解

  • nil :表示空值,只有一个值 nil。将变量赋值为 nil 相当于删除该变量。
  • boolean :有两个值 truefalse。注意:在 Lua 中,只有 nilfalse 被视为假,其他所有值(包括 0、空字符串、空表)都被视为真。
  • number:表示数字,Lua 5.3 之前只有双精度浮点数,Lua 5.3 引入了整数类型。
  • string:表示字符串,是不可变的。可以用单引号、双引号或双方括号(用于多行字符串)表示。
Lua 复制代码
local str1 = 'hello'
local str2 = "world"
local str3 = [[
这是一个
多行字符串
]]

-- 字符串拼接用 .. 运算符
local hello_world = str1 .. " " .. str2
print(hello_world) -- 输出:hello world

1.2 运算符

Lua 的运算符包括算术运算符、关系运算符、逻辑运算符、连接运算符和长度运算符。其中,逻辑运算符的行为比较特殊,需要特别注意。

(1)逻辑运算符的短路求值

Lua 的逻辑运算符 andor 不会返回布尔值,而是返回操作数本身:

  • a and b:如果 a 为真,返回 b;否则返回 a
  • a or b:如果 a 为真,返回 a;否则返回 b

这个特性非常有用,可以用来实现默认值:

Lua 复制代码
-- 如果 x 为 nil 或 false,将其设为 10
local x = x or 10

(2)长度运算符

长度运算符 # 用于获取字符串的长度或 table 的数组部分的长度。注意:# 只对连续的数组部分有效,遇到 nil 会停止计数。

Lua 复制代码
local arr1 = {1, 2, 3, 4, 5}
print(#arr1) -- 输出:5

local arr2 = {1, 2, nil, 4, 5}
print(#arr2) -- 输出:2(遇到 nil 停止)

1.3 控制结构

Lua 提供了 if-elseif-elsewhilerepeat-untilfor 四种控制结构。

(1)for 循环

Lua 有两种 for 循环:数值 for 和泛型 for。

数值 for
Lua 复制代码
-- 从 1 到 10,步长为 1
for i = 1, 10 do
    print(i)
end

-- 从 10 到 1,步长为 -1
for i = 10, 1, -1 do
    print(i)
end
泛型 for

泛型 for 通过迭代器函数遍历集合。Lua 提供了两个常用的迭代器:ipairspairs

  • ipairs:遍历 table 的数组部分,按顺序遍历,遇到 nil 停止
  • pairs:遍历 table 的所有键值对,顺序不定
Lua 复制代码
local t = {1, 2, 3, a = 4, b = 5}

-- 用 ipairs 遍历数组部分
print("ipairs:")
for i, v in ipairs(t) do
    print(i, v) -- 输出:1 1; 2 2; 3 3
end

-- 用 pairs 遍历所有键值对
print("pairs:")
for k, v in pairs(t) do
    print(k, v) -- 输出:1 1; 2 2; 3 3; a 4; b 5(顺序不定)
end

二、Table:Lua 的万能数据结构

Table 是 Lua 中唯一的复杂数据结构,它既可以作为数组使用,也可以作为哈希表使用,还可以用来模拟对象、类、命名空间等。

2.1 Table 的基本使用

Lua 复制代码
-- 创建一个空 table
local t1 = {}

-- 创建一个数组(数字索引从 1 开始!)
local arr = {10, 20, 30, 40, 50}
print(arr[1]) -- 输出:10(注意不是 0)

-- 创建一个哈希表
local dict = {
    name = "Lua",
    version = "5.4",
    author = "Roberto Ierusalimschy"
}
print(dict.name) -- 输出:Lua
print(dict["version"]) -- 输出:5.4

-- 混合使用数组和哈希表
local mixed = {1, 2, 3, name = "mixed", type = "table"}

2.2 Table 的常用操作

Lua 标准库提供了一些常用的 table 操作函数:

Lua 复制代码
local t = {1, 2, 3}

-- 在末尾插入元素
table.insert(t, 4)
print(table.concat(t, ", ")) -- 输出:1, 2, 3, 4

-- 在指定位置插入元素
table.insert(t, 2, 1.5)
print(table.concat(t, ", ")) -- 输出:1, 1.5, 2, 3, 4

-- 删除指定位置的元素
table.remove(t, 2)
print(table.concat(t, ", ")) -- 输出:1, 2, 3, 4

-- 排序
local nums = {3, 1, 4, 1, 5, 9, 2, 6}
table.sort(nums)
print(table.concat(nums, ", ")) -- 输出:1, 1, 2, 3, 4, 5, 6, 9

2.3 元表(Metatable)与元方法(Metamethod)

元表是 Lua 最强大的特性之一,它允许我们改变 table 的行为。元表是一个普通的 table,包含了各种元方法,当对 table 执行某些操作时,Lua 会自动调用对应的元方法。

(1)__index 元方法

当访问 table 中不存在的键时,Lua 会调用该 table 的元表的 __index 元方法。__index 可以是一个函数,也可以是另一个 table。

Lua 复制代码
-- 用 __index 实现默认值
local default_mt = {__index = function(t, k) return "default" end}
local t = setmetatable({a = 1, b = 2}, default_mt)

print(t.a) -- 输出:1
print(t.b) -- 输出:2
print(t.c) -- 输出:default(不存在的键返回默认值)

(2)__add 元方法

当对两个 table 使用 + 运算符时,Lua 会调用第一个 table 的元表的 __add 元方法。

Lua 复制代码
-- 实现向量相加
local Vector = {}
Vector.__add = function(v1, v2)
    return {v1[1] + v2[1], v1[2] + v2[2]}
end

local v1 = setmetatable({1, 2}, Vector)
local v2 = setmetatable({3, 4}, Vector)
local v3 = v1 + v2

print(v3[1], v3[2]) -- 输出:4 6

(3)__tostring 元方法

当将 table 转换为字符串时(比如 print 函数),Lua 会调用元表的 __tostring 元方法。

Lua 复制代码
local Person = {}
Person.__tostring = function(p)
    return string.format("Person{name=%s, age=%d}", p.name, p.age)
end

local p = setmetatable({name = "Alice", age = 25}, Person)
print(p) -- 输出:Person{name=Alice, age=25}

三、函数:一等公民的魅力

在 Lua 中,函数是一等公民,这意味着函数可以作为参数传递给其他函数,可以作为返回值从函数中返回,也可以存储在变量中。

3.1 多返回值

Lua 允许函数返回多个值,这是一个非常方便的特性。

Lua 复制代码
-- 同时返回最大值和最小值
local function max_min(a, b)
    if a > b then
        return a, b
    else
        return b, a
    end
end

local max, min = max_min(10, 20)
print(max, min) -- 输出:20 10

3.2 可变参数

... 表示可变参数,可以用**{...}** 将可变参数转换为 table,或者用 select 函数获取指定位置的参数。

Lua 复制代码
-- 计算任意多个数的和
local function sum(...)
    local total = 0
    for _, v in ipairs({...}) do
        total = total + v
    end
    return total
end

print(sum(1, 2, 3, 4, 5)) -- 输出:15

3.3 闭包(Closure)

如果一个函数内部定义了另一个函数,并且内部函数访问了外部函数的局部变量,那么内部函数就是一个闭包。闭包会捕获外部函数的局部变量,即使外部函数已经执行完毕,这些变量依然存在。

Lua 复制代码
-- 实现计数器
local function create_counter()
    local count = 0
    return function()
        count = count + 1
        return count
    end
end

local counter1 = create_counter()
print(counter1()) -- 输出:1
print(counter1()) -- 输出:2
print(counter1()) -- 输出:3

local counter2 = create_counter()
print(counter2()) -- 输出:1(独立的计数器)

3.4 尾调用优化

当一个函数的最后一个动作是调用另一个函数时,Lua 不会创建新的栈帧,而是复用当前的栈帧,这就是尾调用优化。尾调用优化可以避免递归调用时的栈溢出问题。

Lua 复制代码
-- 用尾调用实现阶乘
local function factorial(n, acc)
    acc = acc or 1
    if n <= 1 then
        return acc
    end
    return factorial(n - 1, n * acc) -- 尾调用
end

print(factorial(1000)) -- 不会栈溢出

3.5 常见的工具函数

(1)tonumber:安全的数值类型转换

tonumber 是 Lua 唯一的官方数值转换函数,用于将其他类型(主要是字符串)的值转换为 number 类型。与其他语言的强制转换不同,Lua 的 tonumber 采用失败返回 nil 的设计,而非抛出异常,这让它在处理不确定输入时非常安全。

1.1 基本语法
Lua 复制代码
tonumber(value [, base])
  • 参数
    • value:要转换的值,可以是字符串、数字、布尔值等任意类型
    • base(可选):进制基数,范围 2~36,默认值为 10
  • 返回值
    • 转换成功:返回对应的 number 类型值
    • 转换失败:返回 nil不会抛出任何错误
1.2 基础用法示例
Lua 复制代码
-- 字符串转数字(默认十进制)
print(tonumber("123"))      -- 123
print(tonumber("-45.67"))   -- -45.67
print(tonumber("  89  "))   -- 89(自动忽略前后空白字符)

-- 布尔值转换
print(tonumber(true))       -- 1
print(tonumber(false))      -- 0

-- nil 转换
print(tonumber(nil))        -- nil

-- 转换失败的情况
print(tonumber("abc123"))   -- nil(包含非数字字符)
print(tonumber("123abc"))   -- nil(即使开头是数字也不行)
print(tonumber({}))         -- nil(table 无法转换)
print(tonumber(function() end)) -- nil(函数无法转换)
1.3 强大的进制转换功能

tonumber 最实用的特性是支持任意进制(2~36)的字符串转十进制数字,这在处理二进制、十六进制数据时非常方便。

Lua 复制代码
-- 二进制转十进制
print(tonumber("1010", 2))  -- 10

-- 八进制转十进制
print(tonumber("777", 8))   -- 511

-- 十六进制转十进制(大小写不敏感)
print(tonumber("FF", 16))   -- 255
print(tonumber("0xff", 16)) -- 255(支持 0x 前缀)

-- 三十六进制转十进制(包含字母 a-z)
print(tonumber("z", 36))    -- 35
print(tonumber("10", 36))   -- 36

-- 超出进制范围的字符会转换失败
print(tonumber("2", 2))     -- nil(二进制只能包含 0 和 1)
print(tonumber("g", 16))    -- nil(十六进制只能包含 0-9 和 a-f)
1.4 常见坑与最佳实践
  1. 必须检查返回值是否为 nil 这是最容易犯的错误。如果直接使用 tonumber 的结果而不检查 nil,后续代码会因为对 nil 进行算术运算而崩溃。

    Lua 复制代码
    -- 错误写法
    local num = tonumber(input)
    local result = num + 10 -- 如果 input 不是数字,这里会报错:attempt to perform arithmetic on a nil value
    
    -- 正确写法
    local num = tonumber(input)
    if not num then
        error("无效的数字输入: " .. tostring(input))
    end
    local result = num + 10
  2. 不要用 tonumber 判断是否为数字 对于已经是 number 类型的值,tonumber 会直接返回它本身,所以可以用 type(x) == "number" 来判断类型,而不是 tonumber(x) ~= nil

  3. 空字符串和空白字符串的区别

    Lua 复制代码
    print(tonumber(""))    -- nil(空字符串转换失败)
    print(tonumber("   ")) -- nil(全空白字符串也转换失败)

(2)pcall:保护式函数调用

Lua 是一门动态语言,运行时错误非常常见。如果直接调用一个可能出错的函数,错误会向上传播直到程序崩溃。pcall (protected call)的作用就是在保护模式下调用函数,捕获所有运行时错误,让程序可以继续执行。

2.1 基本语法
Lua 复制代码
success, result1, result2, ... = pcall(func, arg1, arg2, ...)
  • 参数
    • func:要调用的函数(注意:是函数本身,不是函数调用的结果
    • arg1, arg2, ...:传递给 func 的参数
  • 返回值
    • 第一个返回值:布尔值,表示函数是否执行成功
    • 后续返回值:如果成功,是函数的所有返回值;如果失败,是错误信息
2.2 基础用法示例
Lua 复制代码
-- 定义一个可能出错的函数
local function divide(a, b)
    if b == 0 then
        error("除数不能为零") -- 主动抛出错误
    end
    return a / b
end

-- 成功调用
local ok, result = pcall(divide, 10, 2)
print(ok, result) -- true 5

-- 失败调用
local ok, err = pcall(divide, 10, 0)
print(ok, err) -- false 除数不能为零
2.3 传递多个参数和接收多个返回值

pcall 支持传递任意数量的参数给被调用函数,也能接收函数的多个返回值:

Lua 复制代码
local function multi_return(a, b)
    return a + b, a - b, a * b
end

local ok, sum, diff, product = pcall(multi_return, 10, 5)
if ok then
    print(sum, diff, product) -- 15 5 50
end
2.4 常见坑
  1. 不要把函数调用传给 pcall 这是最致命的错误。如果写成 pcall(func(10, 2)) ,Lua 会先执行 func(10, 2) ,如果出错,错误会在 pcall 执行前就抛出,pcall 根本无法捕获。

    Lua 复制代码
    -- 错误写法
    local ok, err = pcall(divide(10, 0)) -- 这里会直接报错,pcall 不会执行
    
    -- 正确写法
    local ok, err = pcall(divide, 10, 0)
  2. pcall 无法捕获语法错误 pcall 只能捕获运行时错误 ,语法错误在编译阶段就会发生,无法被 pcall 捕获。

    复制代码
    -- 这个错误无法被捕获,因为语法错误在编译时就发生了
    local ok, err = pcall(function()
        local a = 1 + -- 语法错误:缺少右操作数
    end)
  3. pcall 会清空堆栈信息 这是 pcall 最大的缺点:当函数出错时,pcall 只会返回错误信息字符串,不会保留堆栈跟踪,这让调试变得非常困难。要解决这个问题,需要使用 xpcall

(3)xpcall:带错误处理的保护调用

xpcall (extended protected call)是 pcall 的增强版,它允许你指定一个错误处理函数,在错误发生时被调用。错误处理函数可以获取完整的堆栈信息,或者执行清理工作(比如关闭文件、释放资源)。

3.1 基本语法
Lua 复制代码
success, result1, result2, ... = xpcall(func, error_handler, arg1, arg2, ...)
  • 参数
    • func:要调用的函数
    • error_handler:错误处理函数,当 func 出错时会被调用,参数是错误信息
    • arg1, arg2, ...:传递给 func 的参数
  • 返回值 :和 pcall 完全相同
3.2 打印堆栈跟踪的标准写法

这是 xpcall 最常用的场景,利用 debug.traceback 函数获取完整的堆栈信息:

Lua 复制代码
local function risky_function()
    local a = nil
    return a + 1 -- 这里会出错:attempt to perform arithmetic on a nil value
end

-- 错误处理函数:打印错误信息和堆栈跟踪
local function error_handler(err)
    return err .. "\n" .. debug.traceback()
end

local ok, err = xpcall(risky_function, error_handler)
if not ok then
    print("发生错误:")
    print(err)
end

输出结果会包含完整的调用栈,方便定位错误位置:

bash 复制代码
发生错误:
attempt to perform arithmetic on a nil value
stack traceback:
    [C]: in metamethod '__add'
    test.lua:3: in function <test.lua:2>
    [C]: in function 'xpcall'
    test.lua:10: in main chunk
    [C]: in ?
3.3 错误处理函数的注意事项
  1. 错误处理函数本身如果出错,xpcall 不会再捕获这个错误,而是直接抛出。
  2. 错误处理函数的返回值会作为 xpcall 的第二个返回值(即错误信息)。
  3. 错误处理函数是在错误发生的上下文环境中执行的,可以访问出错时的局部变量(但不建议这么做)。

四、面向对象编程:用 Table 模拟类与继承

Lua 本身没有内置的类和对象机制,但可以用 table 和元表轻松模拟面向对象编程。

4.1 类的实现

我们可以将类定义为一个包含方法和构造函数的 table,然后通过元表让实例继承类的方法。

Lua 复制代码
-- 定义 Person 类
local Person = {}
Person.__index = Person

-- 构造函数
function Person.new(name, age)
    local self = setmetatable({}, Person)
    self.name = name
    self.age = age
    return self
end

-- 方法
function Person:say_hello()
    print(string.format("Hello, my name is %s, I'm %d years old.", self.name, self.age))
end

-- 创建实例
local alice = Person.new("Alice", 25)
local bob = Person.new("Bob", 30)

alice:say_hello() -- 输出:Hello, my name is Alice, I'm 25 years old.
bob:say_hello() -- 输出:Hello, my name is Bob, I'm 30 years old.

4.2 继承的实现

通过将子类的元表的 __index 指向父类,可以实现继承。

Lua 复制代码
-- 定义 Student 类,继承自 Person
local Student = {}
Student.__index = Student
setmetatable(Student, Person) -- 继承 Person

-- 构造函数
function Student.new(name, age, grade)
    local self = Person.new(name, age) -- 调用父类构造函数
    setmetatable(self, Student)
    self.grade = grade
    return self
end

-- 重写方法
function Student:say_hello()
    print(string.format("Hello, my name is %s, I'm %d years old, I'm in grade %d.", 
        self.name, self.age, self.grade))
end

-- 新增方法
function Student:study()
    print(string.format("%s is studying in grade %d.", self.name, self.grade))
end

-- 创建实例
local charlie = Student.new("Charlie", 15, 9)
charlie:say_hello() -- 输出:Hello, my name is Charlie, I'm 15 years old, I'm in grade 9.
charlie:study() -- 输出:Charlie is studying in grade 9.

五、协程(Coroutine):轻量级线程

Lua 的协程是一种轻量级的线程,和操作系统线程不同,协程是用户态的,由 Lua 虚拟机调度,开销非常小。一个 Lua 程序可以同时运行成千上万个协程。

5.1 协程的基本操作

协程有四种状态:suspended(挂起)running(运行)normal(正常)dead(死亡)

Lua 复制代码
-- 创建协程
local co = coroutine.create(function(a, b)
    print("co: ", a, b)
    local c = coroutine.yield(a + b) -- 挂起协程,返回 a+b
    print("co: ", c)
    return a * b
end)

-- 启动协程
local success, result = coroutine.resume(co, 10, 20)
print("main: ", success, result) -- 输出:co: 10 20; main: true 30

-- 恢复协程
success, result = coroutine.resume(co, 30)
print("main: ", success, result) -- 输出:co: 30; main: true 200

5.2 生产者 - 消费者模型

协程非常适合实现生产者 - 消费者模型,生产者生产数据,然后通过**yield** 交出控制权,消费者消费数据,然后通过 resume 恢复生产者。

Lua 复制代码
-- 生产者
local function producer()
    for i = 1, 5 do
        print("Produced: ", i)
        coroutine.yield(i) -- 生产数据,挂起
    end
end

-- 消费者
local function consumer(prod)
    while true do
        local success, data = coroutine.resume(prod)
        if not success or data == nil then
            break
        end
        print("Consumed: ", data)
    end
end

-- 运行
local prod = coroutine.create(producer)
consumer(prod)

六、企业级实战:Lua 在 Redis 中的应用

Redis 是目前最流行的内存数据库,它内置了 Lua 脚本支持。Lua 脚本在 Redis 服务器端原子性执行,不会被其他命令打断,非常适合实现复杂的原子操作。

6.1 分布式锁的实现

用 Lua 脚本可以实现一个原子性的分布式锁,避免多个客户端同时获取锁。

Lua 复制代码
-- 获取锁
-- KEYS[1]: 锁的 key
-- ARGV[1]: 锁的值(用于释放锁时验证)
-- ARGV[2]: 过期时间(毫秒)
local lock_key = KEYS[1]
local lock_value = ARGV[1]
local expire_time = ARGV[2]

-- 如果锁不存在,设置锁并设置过期时间
if redis.call("SET", lock_key, lock_value, "NX", "PX", expire_time) then
    return 1
else
    return 0
end
Lua 复制代码
-- 释放锁
-- KEYS[1]: 锁的 key
-- ARGV[1]: 锁的值(必须和获取锁时的值一致)
local lock_key = KEYS[1]
local lock_value = ARGV[1]

-- 如果锁存在且值匹配,删除锁
if redis.call("GET", lock_key) == lock_value then
    redis.call("DEL", lock_key)
    return 1
else
    return 0
end

6.2 原子计数器

用 Lua 脚本可以实现一个原子性的计数器,避免并发问题。

Lua 复制代码
-- 原子递增计数器
-- KEYS[1]: 计数器的 key
-- ARGV[1]: 递增的步长(可选,默认 1)
local counter_key = KEYS[1]
local step = tonumber(ARGV[1]) or 1

local new_value = redis.call("INCRBY", counter_key, step)
return new_value

6.3 Redis Lua 脚本中的错误处理

在 Redis 中执行 Lua 脚本时,任何未被捕获的错误都会导致脚本终止,并返回错误给客户端。因此,pcall 和 **xpcall**在 Redis 脚本中非常重要。

Lua 复制代码
-- Redis 脚本:安全的原子递增操作
local key = KEYS[1]
local step = tonumber(ARGV[1]) or 1

-- 用 pcall 保护 Redis 命令调用
local ok, result = pcall(redis.call, "INCRBY", key, step)
if not ok then
    -- 如果出错,返回错误信息
    return redis.error_reply("递增失败: " .. result)
end

return result

6.4 配置文件解析中的类型转换

在解析配置文件时,输入通常是字符串,需要用 **tonumber**安全地转换为数值类型:

Lua 复制代码
local config = {
    port = "8080",
    timeout = "3000",
    max_connections = "100"
}

-- 安全转换配置值
local port = tonumber(config.port)
if not port or port < 1 or port > 65535 then
    error("无效的端口号: " .. tostring(config.port))
end

local timeout = tonumber(config.timeout) or 5000 -- 提供默认值

七、性能优化与最佳实践

7.1 性能优化技巧

  1. 尽量使用局部变量 :局部变量的访问速度比全局变量快 30% 以上,因为全局变量需要在 _G 表中进行哈希查找。
  2. 预分配 table 大小 :当知道 table 的大致大小时,用 table.create(narray, nhash) 预分配空间,避免动态扩容的开销。
  3. 避免频繁的字符串拼接 :Lua 的字符串是不可变的,每次拼接都会创建新的字符串。拼接大量字符串时,应该使用 table.concat
  4. 减少函数调用开销:将常用的函数赋值给局部变量,避免每次调用都进行全局查找。
  5. 合理使用协程:用协程实现异步操作,避免阻塞,提高程序的并发性能。

7.2 常见坑与最佳实践

  1. 不要用 # 计算包含 nil 的数组长度# 运算符遇到 nil 会停止计数,应该自己维护一个长度变量。
  2. 避免全局变量污染 :所有变量都应该用 local 声明,模块只导出必要的接口。
  3. 注意闭包的内存泄漏:闭包会捕获外部变量,如果闭包被长期持有,外部变量也不会被垃圾回收。
  4. 正确使用 ipairspairsipairs 只遍历数组部分,pairs 遍历所有键值对。
  5. 错误处理要及时 :用 pcallxpcall 捕获可能出错的操作,避免程序崩溃。

八、总结

Lua 是一门设计优雅、功能强大的轻量级脚本语言。它的核心特性 ------ table、函数、元表、协程,共同构成了一个灵活而强大的编程模型。虽然 Lua 不是一门全能的语言,但在嵌入式、高性能、轻量级的场景下,它有着不可替代的优势。

相关推荐
wapicn992 天前
API接口调试笔记:从注册到第一个数据返回,全流程详解
java·开发语言·python·lua
kebeiovo3 天前
C++与 Lua的交互
c++·lua
wh_xia_jun4 天前
Apifox 测试项目实操1
开发语言·lua
颖火虫盟主4 天前
Lua 协程:从 API 到底层原理再到 Skynet 架构的完整学习路径
学习·架构·lua
tongluowan0074 天前
Redisson的参数及工作原理
java·redis·lua·分布式锁
上海合宙LuatOS5 天前
Air8000低功耗指南
开发语言·物联网·php·lua
wh_xia_jun5 天前
HttpRunner 编写测试用例
开发语言·lua
乐于分享的阿乐5 天前
【2026最新】Postman新旧版本下载安装+免登录 保姆级教程
测试工具·lua·postman
彭于晏Yan6 天前
自定义注解+Lua脚本实现限流
java·spring boot·后端·lua