Lua 教程:语法,函数,表和元表详解
Lua 教程:语法、函数、表和元表详解
Lua 是一种轻量级、可嵌入的脚本语言,广泛应用于游戏开发、Web 应用、嵌入式系统等领域。它的设计目标是简洁、高效和可扩展。本教程将深入探讨 Lua 的核心概念,包括基本语法、函数、表(Table)以及元表(Metatable),并通过丰富的示例帮助您掌握 Lua 编程。
1. Lua 基础语法
1.1. 注释
Lua 支持两种注释方式:
- 单行注释: 使用
--
开头,直到行尾。
lua
-- 这是一行单行注释
- 多行注释: 使用
--[[
开头,]]
结尾。
lua
--[[
这是多行注释
可以跨越多行
]]
1.2. 变量和数据类型
Lua 是一种动态类型语言,这意味着您不需要显式声明变量的类型。变量可以存储不同类型的值。Lua 的基本数据类型包括:
- nil: 表示空值或无效值。
- boolean: 布尔值,
true
或false
。 - number: 数字,可以是整数或浮点数(Lua 5.3 引入了整数类型,但通常情况下不需要区分)。
- string: 字符串,用单引号 (
'
) 或双引号 ("
) 括起来。 - table: 表,Lua 中唯一的数据结构,可以表示数组、字典、集合等。
- function: 函数,Lua 中的函数是“一等公民”,可以作为变量传递和使用。
- userdata: 用户数据,用于存储 C 数据或其他外部数据。
- thread: 线程,用于实现协程。
变量声明和赋值:
lua
local a = 10 -- 局部变量 a,赋值为 10
b = "Hello" -- 全局变量 b,赋值为 "Hello"
local c = nil -- 局部变量 c,赋值为 nil
local d = true -- 局部变量 d,赋值为 true
local e = "World" --局部变量e, 赋值为 "World"
注意:
- 使用
local
关键字声明的变量是局部变量,作用域仅限于声明它的代码块(例如函数内部或控制结构内部)。 - 不使用
local
声明的变量是全局变量,在整个 Lua 环境中都可见。 - 未初始化的局部变量默认为
nil
。
1.3. 运算符
Lua 支持常见的算术、关系、逻辑和位运算符:
- 算术运算符:
+
(加),-
(减),*
(乘),/
(除),%
(取模),^
(幂) - 关系运算符:
==
(等于),~=
(不等于),>
(大于),<
(小于),>=
(大于等于),<=
(小于等于) - 逻辑运算符:
and
(与),or
(或),not
(非) - 连接运算符:
..
(字符串连接) - 长度运算符:
#
(获取字符串或表的长度)
```lua
local x = 10
local y = 5
print(x + y) -- 15
print(x - y) -- 5
print(x * y) -- 50
print(x / y) -- 2.0
print(x % y) -- 0
print(x ^ y) -- 100000
print(x == y) -- false
print(x ~= y) -- true
print(x > y) -- true
print(true and false) -- false
print(true or false) -- true
print(not true) -- false
local str1 = "Hello"
local str2 = "World"
print(str1 .. " " .. str2) -- "Hello World"
local arr = {1, 2, 3, 4, 5}
print(#arr) -- 5
print(#str1) -- 5
```
1.4. 控制结构
Lua 提供了常见的控制结构,包括条件语句、循环语句和跳转语句:
- if-elseif-else:
```lua
local age = 18
if age < 18 then
print("未成年")
elseif age >= 18 and age < 60 then
print("成年")
else
print("老年")
end
```
- while:
lua
local i = 1
while i <= 5 do
print(i)
i = i + 1
end
- repeat-until:
lua
local i = 1
repeat
print(i)
i = i + 1
until i > 5
- for (数值 for):
```lua
for i = 1, 5 do -- 从 1 到 5,步长为 1(默认)
print(i)
end
for i = 1, 10, 2 do -- 从 1 到 10,步长为 2
print(i)
end
```
- for (泛型 for): 遍历表或迭代器。
```lua
local arr = {10, 20, 30, 40, 50}
-- 遍历数组(ipairs 用于遍历数组)
for i, v in ipairs(arr) do
print(i, v) -- i 是索引,v 是值
end
local dict = {name = "Alice", age = 30, city = "New York"}
-- 遍历字典(pairs 用于遍历字典)
for k, v in pairs(dict) do
print(k, v) -- k 是键,v 是值
end
```
- break: 用于跳出循环。
lua
for i = 1, 10 do
if i == 5 then
break -- 当 i 等于 5 时跳出循环
end
print(i)
end
- goto (Lua 5.2 及以上版本):
lua
local i = 1
::loop_start:: -- 定义标签
print(i)
i = i + 1
if i <= 5 then
goto loop_start -- 跳转到标签处
end
注意:goto
语句应谨慎使用,因为它可能导致代码难以理解和维护。
2. 函数
函数在 Lua 中是“一等公民”,这意味着它们可以像其他值一样被赋值给变量、作为参数传递给其他函数、作为返回值返回。
2.1. 函数定义
```lua
-- 定义一个简单的函数
function add(a, b)
return a + b
end
-- 将函数赋值给变量
local my_add = add
print(my_add(5, 3)) -- 8
-- 匿名函数
local multiply = function(a, b)
return a * b
end
print(multiply(4, 6)) -- 24
```
2.2. 函数参数
- 固定参数:
```lua
function greet(name, age)
print("Hello, " .. name .. "! You are " .. age .. " years old.")
end
greet("Bob", 25)
```
- 可变参数: 使用
...
表示可变参数列表。
```lua
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
```
2.3. 函数返回值
Lua 函数可以返回多个值。
```lua
function get_info()
local name = "Alice"
local age = 30
local city = "New York"
return name, age, city
end
local n, a, c = get_info()
print(n, a, c) -- "Alice" 30 "New York"
```
2.4 闭包
闭包是指一个函数和其相关的引用环境的组合。 这意味着函数可以访问并操作其外部作用域中的变量,即使在外部函数已经返回之后。
```lua
function outerFunction(x)
local innerValue = 10
-- 内部函数(闭包)
local innerFunction = function(y)
return x + y + innerValue
end
return innerFunction
end
local closure = outerFunction(5)
print(closure(3)) -- 输出: 18 (5 + 3 + 10)
-- outerFunction 返回后,closure 仍然可以访问 x 和 innerValue
``
innerFunction
在上述例子中,是一个闭包,因为它引用了其外部函数
outerFunction的变量
x和
innerValue。即使
outerFunction已经返回,
closure` 仍然可以访问和使用这些变量。
3. 表 (Table)
表是 Lua 中唯一的数据结构,它非常灵活,可以用来表示数组、字典(关联数组)、集合、记录等。
3.1. 表的创建
```lua
-- 创建一个空表
local my_table = {}
-- 创建一个带有初始值的表(数组形式)
local arr = {10, 20, 30, "hello", true}
-- 创建一个带有初始值的表(字典形式)
local dict = {
name = "Alice",
age = 30,
city = "New York",
["favorite color"] = "blue" -- 键可以是字符串或任何其他类型的值
}
```
3.2. 表的访问
```lua
-- 数组形式的访问
print(arr[1]) -- 10 (注意:Lua 数组的索引从 1 开始)
print(arr[4]) -- "hello"
-- 字典形式的访问
print(dict.name) -- "Alice"
print(dict["age"]) -- 30
print(dict["favorite color"]) -- "blue"
```
3.3. 表的操作
- 插入元素:
```lua
-- 在数组末尾插入元素
table.insert(arr, 40)
table.insert(arr, "world")
-- 在指定位置插入元素
table.insert(arr, 3, 25) -- 在索引 3 处插入 25
-- 给字典添加键值对
dict.country = "USA"
dict["occupation"] = "Engineer"
```
- 删除元素:
```lua
-- 删除数组末尾的元素
table.remove(arr)
-- 删除指定位置的元素
table.remove(arr, 2) -- 删除索引 2 处的元素
-- 删除字典中的键值对
dict.age = nil -- 将 age 键的值设置为 nil,相当于删除
```
- 获取表的长度:
lua
print(#arr) -- 获取数组的长度
-- 注意:# 运算符不适用于字典形式的表(结果未定义)
-- 对于字典形式的表,通常使用循环来计算键值对的数量
- 遍历表:
```lua
--遍历数组
for index, value in ipairs(arr) do
print ("index: " .. index .. " value: " .. value)
end
--遍历table
for key, value in pairs(dict) do
print ("key: " .. key .. " value: " .. value)
end
```
4. 元表 (Metatable)
元表是 Lua 中一种强大的机制,它允许您修改表和用户数据的行为。通过设置元表,您可以自定义运算符的行为、控制访问、实现面向对象编程等。
4.1. 设置和获取元表
- setmetatable(table, metatable): 将
metatable
设置为table
的元表。 - getmetatable(table): 获取
table
的元表(如果存在)。
```lua
local my_table = {}
local my_metatable = {}
setmetatable(my_table, my_metatable)
local mt = getmetatable(my_table)
print(mt == my_metatable) -- true
```
4.2. 元方法 (Metamethods)
元表中的字段称为元方法,它们是一些特殊的函数,用于定义表在特定操作下的行为。以下是一些常用的元方法:
__index
: 当访问表中不存在的键时调用。__newindex
: 当给表中不存在的键赋值时调用。__add
: 定义+
运算符的行为。__sub
: 定义-
运算符的行为。__mul
: 定义*
运算符的行为。__div
: 定义/
运算符的行为。__mod
: 定义%
运算符的行为。__pow
: 定义^
运算符的行为。__unm
: 定义一元-
运算符的行为。__concat
: 定义..
运算符的行为。__len
: 定义#
运算符的行为。__eq
: 定义==
运算符的行为。__lt
: 定义<
运算符的行为。__le
: 定义<=
运算符的行为。__tostring
: 定义表转换为字符串时的行为 (例如print(table)
).__call
: 当将表作为函数调用时触发。
4.3 示例
4.3.1. 使用 __index
实现默认值
```lua
local default_values = {
name = "Unknown",
age = 0,
city = "N/A"
}
local my_metatable = {
__index = default_values
}
local person = {}
setmetatable(person, my_metatable)
print(person.name) -- "Unknown" (person 表中没有 name 键,所以访问 default_values)
print(person.age) -- 0
print(person.city) -- "N/A"
person.name = "Alice" -- 给 person 表设置 name 键
print(person.name) -- "Alice" (现在 person 表有 name 键了)
`__index` 也可以是一个函数。
lua
local my_metatable = {
__index = function(table, key)
print("访问了不存在的键: " .. key)
return "默认值" -- 返回默认值
end
}
local my_table = {}
setmetatable(my_table, my_metatable)
print(my_table.some_key) -- 输出: 访问了不存在的键: some_key 默认值
```
4.3.2. 使用 __add
实现向量加法
```lua
local vector_mt = {
__add = function(v1, v2)
local result = {}
for i = 1, #v1 do
result[i] = v1[i] + v2[i]
end
return result
end
}
local v1 = {1, 2, 3}
local v2 = {4, 5, 6}
setmetatable(v1, vector_mt)
setmetatable(v2, vector_mt)
local v3 = v1 + v2 -- 调用 __add 元方法
for i, v in ipairs(v3) do
print(v) -- 5 7 9
end
```
4.3.3. 使用 __tostring
控制表的字符串表示
lua
local my_metatable = {
__tostring = function (table)
local str = "{"
local first = true
for key, value in pairs (table) do
if not first then
str = str .. ", "
end
str = str .. tostring (key) .. " = " .. tostring (value)
first = false
end
return str .. "}"
end
}
local dict = {name = "Bob", age = "20"}
setmetatable (dict, my_metatable)
print (dict) -- {name = Bob, age = 20}
4.3.4 使用__call
将表作为函数调用
```lua
local my_metatable = {
__call = function(table, ...)
local args = {...}
print("Table called with arguments:")
for i, v in ipairs(args) do
print(i, v)
end
end
}
local my_table = {}
setmetatable(my_table, my_metatable)
my_table(1, "hello", true)
--[[输出:
Table called with arguments:
1 1
2 hello
3 true
]]
```
总结
本教程详细介绍了 Lua 的基本语法、函数、表和元表。掌握这些核心概念是编写 Lua 代码的基础。通过学习和实践,您将能够充分利用 Lua 的特性,编写出简洁、高效和可扩展的程序。Lua 的学习曲线相对平缓,但其功能却非常强大,是值得深入学习的一门语言。