Lua 教程:语法,函数,表和元表详解

Lua 教程:语法、函数、表和元表详解

Lua 是一种轻量级、可嵌入的脚本语言,广泛应用于游戏开发、Web 应用、嵌入式系统等领域。它的设计目标是简洁、高效和可扩展。本教程将深入探讨 Lua 的核心概念,包括基本语法、函数、表(Table)以及元表(Metatable),并通过丰富的示例帮助您掌握 Lua 编程。

1. Lua 基础语法

1.1. 注释

Lua 支持两种注释方式:

  • 单行注释: 使用 -- 开头,直到行尾。

lua
-- 这是一行单行注释

  • 多行注释: 使用 --[[ 开头,]] 结尾。

lua
--[[
这是多行注释
可以跨越多行
]]

1.2. 变量和数据类型

Lua 是一种动态类型语言,这意味着您不需要显式声明变量的类型。变量可以存储不同类型的值。Lua 的基本数据类型包括:

  • nil: 表示空值或无效值。
  • boolean: 布尔值,truefalse
  • 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的变量xinnerValue。即使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 的学习曲线相对平缓,但其功能却非常强大,是值得深入学习的一门语言。

THE END