轻量级脚本语言入门指南
Lua 5.1 详细教程
轻量级脚本语言入门指南
history Lua 历史
由巴西里约热内卢天主教大学的 Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo 于 1993年 设计并实现
stars Lua 特点
• 轻量级:标准C语言编写,编译后仅一百余K
• 可扩展:提供易于使用的扩展接口和机制
• 高效:执行速度快,资源占用少
• 可移植:可在多种操作系统和平台上运行
apps 应用场景
• 游戏开发:如《魔兽世界》、《愤怒的小鸟》等
• 嵌入式系统:作为脚本语言嵌入到应用程序中
• 网络应用:如Nginx的Lua模块、Redis脚本等
• 安全系统:如入侵检测系统
Lua 5.1 基础语法
code 基本语法特点
Lua 5.1 的语法简洁明了,具有以下特点:
comment 注释
Lua 支持单行注释和多行注释:
–[[
这是多行注释
可以跨越多行
–]]
label 标识符
标识符命名规则:
mohd zara abc move_name
a_123 myname50 _temp j
— 非法的标识符
2var my-name my@var
• 以字母、下划线开头,后跟字母、数字或下划线
• 避免使用下划线加大写字母的标识符(Lua保留字格式)
format_quote 关键字
Lua 5.1 的关键字不能用作标识符:
end false for function if
in local nil not or
repeat return then true until while
Lua数据类型
Lua是动态类型语言,变量不需要类型定义,只需要为变量赋值。Lua中有8种基本数据类型,所有类型均为FirstClass的。
nil
表示一个无效值,只有值nil属于该类,在条件表达式中相当于false。可用于删除变量或表中的值。
a = nil
print(type(a)) — nil
boolean
包含两个值:false和true。在Lua中,只有false和nil被视为假,其他值(包括0和空字符串)都被视为真。
print(type(false)) — boolean
if 0 then print(“0被视为真”) end
number
表示双精度类型的实浮点数,Lua 5.1中没有整数类型。可用于数学计算,取值范围大约在1.79e-308到1.79e+308之间。
print(type(3.14)) — number
print(10 + 3.14) — 13.14
string
表示一个字符序列,采用8位编码,可以包含C的转义序列。是不可修改的值对象。可用单引号、双引号或长括号表示。
print(type(‘world’)) — string
print(type([[多行字符串]])) — string
function
由C或Lua编写的函数。在Lua中,函数是”第一类值”,可以存储在变量中,作为参数传递,或作为返回值。
return a + b
end
print(type(add)) — function
table
Lua中的表是一个”关联数组”,索引可以是数字、字符串或表类型。是Lua中唯一的数据结构,可用于实现数组、字典、集合等。
a[“key”] = “value”
a[1] = 100
print(type(a)) — table
userdata
表示任意存储在变量中的C数据结构。用于将C语言中的数据结构传递到Lua中,Lua不能直接操作这些数据,只能通过C API进行操作。
local file = io.open(“test.txt”, “r”)
print(type(file)) — userdata
thread
表示执行的独立线路,用于执行协同程序(coroutine)。Lua中的线程与操作系统中的线程不同,它是协同式的,而非抢占式的。
print(“协同程序”)
end)
print(type(co)) — thread
Lua变量
Lua是动态类型语言,变量不需要声明类型,只需要为变量赋值。Lua中有两种变量:全局变量和局部变量。变量未初始化时值为nil。
全局变量
全局变量无需特殊声明,直接赋值即可创建。全局变量在整个程序中都可访问,除非被局部变量覆盖。
globalVar = “I am global”
— 访问全局变量
print(globalVar) — 输出: I am global
— 删除全局变量
globalVar = nil
print(globalVar) — 输出: nil
— 未声明的变量默认为nil
print(undefinedVar) — 输出: nil
局部变量
局部变量使用local关键字声明,作用域仅限于声明它们的代码块(函数、控制结构或代码块)。局部变量访问速度更快,且不会污染全局命名空间。
local localVar = “I am local”
— 在函数中使用局部变量
function test()
local funcVar = “I am local to function”
print(funcVar)
end
test() — 输出: I am local to function
— print(funcVar) — 错误: 变量未定义
— 局部变量覆盖全局变量
x = “global”
do
local x = “local”
print(x) — 输出: local
end
print(x) — 输出: global
Lua运算符
Lua提供了丰富的运算符,包括算术运算符、关系运算符、逻辑运算符和其他运算符。这些运算符用于执行各种操作,如数学计算、比较和逻辑判断。
算术运算符
用于执行基本的数学运算
运算符 | 描述 |
---|---|
+ | 加法 |
– | 减法 |
* | 乘法 |
/ | 除法 |
^ | 乘方 |
% | 取模 |
b = 3
print(a + b) — 13
print(a – b) — 7
print(a * b) — 30
print(a / b) — 3.333…
print(a ^ b) — 1000
print(a % b) — 1
关系运算符
用于比较两个值,返回布尔值
运算符 | 描述 |
---|---|
== | 等于 |
~= | 不等于 |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
b = 20
print(a == b) — false
print(a ~= b) — true
print(a < b) -- true
print(a > b) — false
print(a <= b) -- true
print(a >= b) — false
逻辑运算符
用于布尔逻辑运算,注意Lua中只有false和nil被视为假
运算符 | 描述 |
---|---|
and | 逻辑与 |
or | 逻辑或 |
not | 逻辑非 |
print(true or false) — true
print(not true) — false
— 短路求值
a = false
b = 10
print(a and b) — false
— 实用技巧
x = x or v — 如果x为false或nil,则x=v
其他运算符
包括连接运算符、取长度运算符和优先级
运算符 | 描述 |
---|---|
.. | 字符串连接 |
# | 取长度 |
str1 = “Hello “
str2 = “World”
print(str1 .. str2) — Hello World
— 取长度
arr = {10, 20, 30}
print(#arr) — 3
— 运算符优先级(从高到低)
— ^
— not # –
— * / %
— + –
— ..
— < > <= >= ~= ==
— and
— or
Lua控制结构
Lua提供了多种控制结构,包括条件语句和循环语句,用于控制程序的执行流程。这些结构使程序能够根据条件执行不同的代码块,或重复执行特定操作。
if 条件语句
用于根据条件执行不同的代码块。Lua支持if、elseif和else结构。
if a > 0 then
print(“a是正数”)
end
— if-else语句
if a > 0 then
print(“a是正数”)
else
print(“a不是正数”)
end
— if-elseif-else语句
if a > 0 then
print(“a是正数”)
elseif a < 0 then
print(“a是负数”)
else
print(“a是零”)
end
while 循环
当条件为真时重复执行代码块。在每次循环开始前检查条件。
local i = 1
while i <= 5 do
print(“循环次数: ” .. i)
i = i + 1
end
— 使用while计算阶乘
local n = 5
local result = 1
local i = 1
while i <= n do
result = result * i
i = i + 1
end
print(n .. “的阶乘是: ” .. result)
repeat 循环
重复执行代码块直到条件为真。与while不同,repeat循环至少执行一次。
local i = 1
repeat
print(“循环次数: ” .. i)
i = i + 1
until i > 5
— 使用repeat循环读取输入
local input
repeat
input = io.read()
if input ~= “quit” then
print(“你输入了: ” .. input)
end
until input == “quit”
for 循环
Lua提供两种for循环:数值for循环和泛型for循环。
for i = 1, 5, 1 do
print(“数值循环: ” .. i)
end
— 泛型for循环(遍历表)
local fruits = {“apple”, “banana”, “orange”}
for i, fruit in ipairs(fruits) do
print(i, fruit)
end
— 泛型for循环(遍历键值对)
local person = {name = “张三”, age = 25}
for key, value in pairs(person) do
print(key, value)
end
Lua函数
在Lua中,函数是第一类值,可以存储在变量中,作为参数传递,或作为返回值。函数是Lua编程的核心组成部分,提供了代码重用和模块化的能力。
函数定义
Lua提供了多种定义函数的方式,最常用的是function关键字。
function add(a, b)
return a + b
end
— 函数赋值给变量
local multiply = function(a, b)
return a * b
end
— 局部函数
local function divide(a, b)
if b == 0 then
return nil, “除数不能为零”
end
return a / b
end
函数调用
Lua中调用函数的方式灵活多样,可以适应不同场景。
local sum = add(10, 20)
print(sum) — 30
— 多返回值函数调用
local result, err = divide(10, 0)
if not result then
print(err) — 除数不能为零
end
— 特殊调用方式
print “Hello” — 等同于 print(“Hello”)
dofile “test.lua” — 等同于 dofile(“test.lua”)
— 将函数作为参数传递
function exec(f, a, b)
return f(a, b)
end
print(exec(add, 5, 3)) — 8
参数传递
Lua函数参数传递灵活,支持可变参数和默认参数。
function sum(…)
local s = 0
for i, v in ipairs{…} do
s = s + v
end
return s
end
print(sum(1, 2, 3, 4)) — 10
— 默认参数模拟
function greet(name)
name = name or “访客”
return “你好, ” .. name
end
print(greet()) — 你好, 访客
print(greet(“张三”)) — 你好, 张三
闭包与高阶函数
Lua支持闭包和高阶函数,这是函数式编程的重要特性。
function createCounter()
local count = 0
return function()
count = count + 1
return count
end
end
local counter1 = createCounter()
local counter2 = createCounter()
print(counter1()) — 1
print(counter1()) — 2
print(counter2()) — 1
— 高阶函数示例
function map(t, f)
local result = {}
for i, v in ipairs(t) do
result[i] = f(v)
end
return result
end
local squares = map({1, 2, 3}, function(x) return x*x end)
print(table.concat(squares, “, “)) — 1, 4, 9
Lua表(Table)
表(Table)是Lua中唯一的数据结构,可以用来实现数组、字典、集合、对象等多种数据结构。表是Lua中最强大和灵活的特性之一,理解表的使用是掌握Lua编程的关键。
表的基础
表是Lua中的关联数组,可以用任意值(除了nil)作为索引。创建表使用构造表达式{}。
local emptyTable = {}
— 创建并初始化表
local person = {
name = “张三”,
age = 25,
gender = “男”
}
— 访问表元素
print(person.name) — 张三
print(person[“age”]) — 25
— 修改表元素
person.age = 26
person[“gender”] = “女”
数组与列表
在Lua中,数组是使用整数索引的表。Lua数组索引从1开始,而不是0。
local fruits = {“apple”, “banana”, “orange”}
— 访问数组元素
print(fruits[1]) — apple
print(fruits[3]) — orange
— 遍历数组
for i = 1, #fruits do
print(fruits[i])
end
— 使用ipairs遍历数组
for i, fruit in ipairs(fruits) do
print(i, fruit)
end
字典与映射
表可以用作字典或映射,使用字符串或其他非整数值作为键。
local config = {
host = “localhost”,
port = 8080,
[“user-agent”] = “Lua/5.1”
}
— 访问字典元素
print(config.host) — localhost
print(config[“port”]) — 8080
— 遍历字典
for key, value in pairs(config) do
print(key, value)
end
— 添加新键值对
config.timeout = 30
表操作与元表
Lua提供了表的标准库函数,以及元表机制,使表具有更强大的功能。
local t = {10, 20, 30}
table.insert(t, 40) — 在末尾插入
table.remove(t, 2) — 删除第二个元素
table.sort(t) — 排序
— 元表示例
local mt = {
__add = function(a, b)
local result = {}
for i = 1, #a do
result[i] = a[i] + b[i]
end
return result
end
}
local a = {1, 2, 3}
local b = {4, 5, 6}
setmetatable(a, mt)
setmetatable(b, mt)
local c = a + b — {5, 7, 9}
Lua模块与包
模块和包是Lua中组织和重用代码的重要机制。模块是一个包含函数和数据的表,可以通过require函数加载。包则是一组相关的模块,通常以目录结构组织。
创建模块
模块本质上是返回一个表的Lua文件,包含函数、变量和数据。
local mymodule = {}
— 私有函数
local function helper()
print(“这是一个私有辅助函数”)
end
— 公共函数
function mymodule.foo()
print(“这是模块的公共函数”)
helper() — 调用私有函数
end
— 公共变量
mymodule.version = “1.0”
— 返回模块表
return mymodule
加载模块
使用require函数加载模块,Lua会自动处理模块路径和缓存。
local mymodule = require(“mymodule”)
— 使用模块功能
mymodule.foo() — 调用模块函数
print(mymodule.version) — 访问模块变量
— 模块缓存
local m1 = require(“mymodule”)
local m2 = require(“mymodule”)
print(m1 == m2) — true,同一个模块实例
— 使用package.path查看模块搜索路径
print(package.path)
包的组织
包是一组相关模块的集合,通常使用点号分隔的命名空间。
— mypackage/
— init.lua
— utils.lua
— net/
— client.lua
— server.lua
— mypackage/init.lua
local mypackage = {}
— 加载子模块
mypackage.utils = require(“mypackage.utils”)
mypackage.client = require(“mypackage.net.client”)
mypackage.server = require(“mypackage.net.server”)
return mypackage
模块加载机制
Lua通过package.path和package.cpath配置模块搜索路径。
— package.path用于Lua模块
— package.cpath用于C模块
— 修改模块搜索路径
package.path = package.path .. “;./modules/?.lua”
— 自定义模块加载器
table.insert(package.loaders, 2, function(moduleName)
— 自定义加载逻辑
local modulePath = moduleName:gsub(“%.”, “/”) .. “.lua”
local file = io.open(modulePath, “r”)
if file then
local content = file:read(“*all”)
file:close()
return loadstring(content, moduleName)
end
return nil
end)
Lua最佳实践
掌握Lua的最佳实践和技巧,能够编写出高效、可维护和优雅的代码。以下是一些在Lua 5.1编程中值得遵循的最佳实践和常用技巧。
性能优化
Lua虽然高效,但合理优化可以进一步提升性能。
local sqrt = math.sqrt
local result = sqrt(100) — 比math.sqrt(100)更快
— 预计算表长度
local t = {10, 20, 30, 40, 50}
local len = #t
for i = 1, len do
— 处理t[i]
end
— 避免在循环中创建表
local temp = {}
for i = 1, 100 do
temp[1] = i * 2
— 使用temp而不是创建新表
end
代码风格
良好的代码风格提高可读性和可维护性。
local userName = “张三” — 驼峰命名法
local MAX_COUNT = 100 — 常量使用大写
local _privateVar = 10 — 私有变量以下划线开头
— 适当的缩进和空格
function calculateSum(a, b)
local result = a + b
if result > 0 then
return result
else
return 0
end
end
— 有意义的注释
— 计算两点之间的距离
function distance(p1, p2)
return math.sqrt((p1.x – p2.x)^2 + (p1.y – p2.y)^2)
end
错误处理
良好的错误处理机制提高程序的健壮性。
function divide(a, b)
assert(b ~= 0, “除数不能为零”)
return a / b
end
— 使用pcall保护可能出错的代码
local status, result = pcall(function()
— 可能出错的代码
return 10 / 0
end)
if not status then
print(“错误:”, result)
end
— 自定义错误处理
function safeCall(f, …)
local success, result = pcall(f, …)
if success then
return result
else
return nil, result
end
end
设计模式
在Lua中应用常见的设计模式,提高代码质量。
local Singleton = {}
local instance = nil
function Singleton:new()
if not instance then
instance = setmetatable({}, self)
self.__index = self
end
return instance
end
— 观察者模式
local Observer = {listeners = {}}
function Observer:subscribe(callback)
table.insert(self.listeners, callback)
end
function Observer:notify(data)
for _, callback in ipairs(self.listeners) do
callback(data)
end
end