Lua 5.1 详细教程

轻量级脚本语言入门指南

Lua 5.1 详细教程

Lua 5.1 详细教程

轻量级脚本语言入门指南

history Lua 历史

由巴西里约热内卢天主教大学的 Roberto IerusalimschyWaldemar CelesLuiz Henrique de Figueiredo1993年 设计并实现

stars Lua 特点

轻量级:标准C语言编写,编译后仅一百余K
可扩展:提供易于使用的扩展接口和机制
高效:执行速度快,资源占用少
可移植:可在多种操作系统和平台上运行

apps 应用场景

游戏开发:如《魔兽世界》、《愤怒的小鸟》等
嵌入式系统:作为脚本语言嵌入到应用程序中
网络应用:如Nginx的Lua模块、Redis脚本等
安全系统:如入侵检测系统


Lua 5.1 基础语法

Lua 5.1 基础语法

code 基本语法特点

Lua 5.1 的语法简洁明了,具有以下特点:

check_circle区分大小写
check_circle语句结束符可选
check_circle动态类型
check_circle变量无需声明

comment 注释

Lua 支持单行注释和多行注释:

— 这是单行注释

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

label 标识符

标识符命名规则:

— 合法的标识符
mohd zara abc move_name
a_123 myname50 _temp j

— 非法的标识符
2var my-name my@var

• 以字母、下划线开头,后跟字母、数字或下划线

避免使用下划线加大写字母的标识符(Lua保留字格式)

format_quote 关键字

Lua 5.1 的关键字不能用作标识符:

and break do else elseif
end false for function if
in local nil not or
repeat return then true until while

Lua数据类型

Lua数据类型

Lua是动态类型语言,变量不需要类型定义,只需要为变量赋值。Lua中有8种基本数据类型,所有类型均为FirstClass的。

block

nil

表示一个无效值,只有值nil属于该类,在条件表达式中相当于false。可用于删除变量或表中的值。

print(type(nil)) — nil
a = nil
print(type(a)) — nil
toggle_on

boolean

包含两个值:false和true。在Lua中,只有false和nil被视为假,其他值(包括0和空字符串)都被视为真。

print(type(true)) — boolean
print(type(false)) — boolean
if 0 then print(“0被视为真”) end
tag

number

表示双精度类型的实浮点数,Lua 5.1中没有整数类型。可用于数学计算,取值范围大约在1.79e-308到1.79e+308之间。

print(type(10)) — number
print(type(3.14)) — number
print(10 + 3.14) — 13.14
text_fields

string

表示一个字符序列,采用8位编码,可以包含C的转义序列。是不可修改的值对象。可用单引号、双引号或长括号表示。

print(type(“hello”)) — string
print(type(‘world’)) — string
print(type([[多行字符串]])) — string
functions

function

由C或Lua编写的函数。在Lua中,函数是”第一类值”,可以存储在变量中,作为参数传递,或作为返回值。

function add(a, b)
  return a + b
end
print(type(add)) — function
table_chart

table

Lua中的表是一个”关联数组”,索引可以是数字、字符串或表类型。是Lua中唯一的数据结构,可用于实现数组、字典、集合等。

a = {}
a[“key”] = “value”
a[1] = 100
print(type(a)) — table
data_object

userdata

表示任意存储在变量中的C数据结构。用于将C语言中的数据结构传递到Lua中,Lua不能直接操作这些数据,只能通过C API进行操作。

— 通常由C API创建
local file = io.open(“test.txt”, “r”)
print(type(file)) — userdata
sync_alt

thread

表示执行的独立线路,用于执行协同程序(coroutine)。Lua中的线程与操作系统中的线程不同,它是协同式的,而非抢占式的。

co = coroutine.create(function ()
  print(“协同程序”)
end)
print(type(co)) — thread

Lua变量

Lua变量

Lua是动态类型语言,变量不需要声明类型,只需要为变量赋值。Lua中有两种变量:全局变量局部变量。变量未初始化时值为nil

public

全局变量

全局变量无需特殊声明,直接赋值即可创建。全局变量在整个程序中都可访问,除非被局部变量覆盖。

— 创建全局变量
globalVar = “I am global”

— 访问全局变量
print(globalVar) — 输出: I am global

— 删除全局变量
globalVar = nil
print(globalVar) — 输出: nil

— 未声明的变量默认为nil
print(undefinedVar) — 输出: nil
lightbulb 全局变量存储在全局环境表中,可通过_G表访问
location_on

局部变量

局部变量使用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
lightbulb 优先使用局部变量,提高代码效率和可维护性

Lua运算符

Lua运算符

Lua提供了丰富的运算符,包括算术运算符关系运算符逻辑运算符和其他运算符。这些运算符用于执行各种操作,如数学计算、比较和逻辑判断。

calculate

算术运算符

用于执行基本的数学运算

运算符 描述
+ 加法
减法
* 乘法
/ 除法
^ 乘方
% 取模
a = 10
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
compare_arrows

关系运算符

用于比较两个值,返回布尔值

运算符 描述
== 等于
~= 不等于
< 小于
> 大于
<= 小于等于
>= 大于等于
a = 10
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
account_tree

逻辑运算符

用于布尔逻辑运算,注意Lua中只有false和nil被视为假

运算符 描述
and 逻辑与
or 逻辑或
not 逻辑非
print(true and false) — false
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
more_horiz

其他运算符

包括连接运算符、取长度运算符和优先级

运算符 描述
.. 字符串连接
# 取长度
— 字符串连接
str1 = “Hello “
str2 = “World”
print(str1 .. str2) — Hello World

— 取长度
arr = {10, 20, 30}
print(#arr) — 3

— 运算符优先级(从高到低)
— ^
— not # –
— * / %
— + –
— ..
— < > <= >= ~= ==
— and
— or

Lua控制结构

Lua控制结构

Lua提供了多种控制结构,包括条件语句循环语句,用于控制程序的执行流程。这些结构使程序能够根据条件执行不同的代码块,或重复执行特定操作。

call_split

if 条件语句

用于根据条件执行不同的代码块。Lua支持if、elseif和else结构。

— 基本if语句
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
lightbulb Lua中只有false和nil被视为假,其他值(包括0)都为真
loop

while 循环

当条件为真时重复执行代码块。在每次循环开始前检查条件。

— 基本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)
warning 确保循环条件最终会变为false,否则会导致无限循环
repeat

repeat 循环

重复执行代码块直到条件为真。与while不同,repeat循环至少执行一次。

— 基本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”
lightbulb repeat-until相当于其他语言中的do-while循环
filter_none

for 循环

Lua提供两种for循环:数值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
lightbulb break语句可用于退出循环,return语句可用于从函数返回

Lua函数

Lua函数

在Lua中,函数是第一类值,可以存储在变量中,作为参数传递,或作为返回值。函数是Lua编程的核心组成部分,提供了代码重用和模块化的能力。

code

函数定义

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
lightbulb 函数可以返回多个值,用逗号分隔
play_arrow

函数调用

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
lightbulb 当函数只有一个参数且是字符串或表构造式时,括号可省略
swap_horiz

参数传递

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(“张三”)) — 你好, 张三
lightbulb 可变参数使用…表示,可以当作表使用{…}
extension

闭包与高阶函数

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
lightbulb 闭包可以记住创建时的环境,实现数据封装

Lua表(Table)

Lua表(Table)

表(Table)是Lua中唯一的数据结构,可以用来实现数组、字典、集合、对象等多种数据结构。表是Lua中最强大和灵活的特性之一,理解表的使用是掌握Lua编程的关键。

view_module

表的基础

表是Lua中的关联数组,可以用任意值(除了nil)作为索引。创建表使用构造表达式{}。

— 创建空表
local emptyTable = {}

— 创建并初始化表
local person = {
  name = “张三”,
  age = 25,
  gender = “男”
}

— 访问表元素
print(person.name) — 张三
print(person[“age”]) — 25

— 修改表元素
person.age = 26
person[“gender”] = “女”
lightbulb 表可以是全局的,也可以是局部的,建议使用局部表
format_list_numbered

数组与列表

在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
lightbulb #运算符返回表的长度,仅适用于连续索引的数组
map

字典与映射

表可以用作字典或映射,使用字符串或其他非整数值作为键。

— 创建字典
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
lightbulb 使用pairs函数遍历表中的所有键值对
settings

表操作与元表

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}
lightbulb 元表允许改变表的行为,实现面向对象编程

Lua模块与包

Lua模块与包

模块和包是Lua中组织和重用代码的重要机制。模块是一个包含函数和数据的表,可以通过require函数加载。包则是一组相关的模块,通常以目录结构组织。

view_module

创建模块

模块本质上是返回一个表的Lua文件,包含函数、变量和数据。

— mymodule.lua 文件
local mymodule = {}

— 私有函数
local function helper()
  print(“这是一个私有辅助函数”)
end

— 公共函数
function mymodule.foo()
  print(“这是模块的公共函数”)
  helper() — 调用私有函数
end

— 公共变量
mymodule.version = “1.0”

— 返回模块表
return mymodule
lightbulb 使用local定义的函数和变量只在模块内部可见
download

加载模块

使用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)
lightbulb require会缓存已加载的模块,避免重复加载
folder

包的组织

包是一组相关模块的集合,通常使用点号分隔的命名空间。

— 包结构示例
— 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
lightbulb init.lua文件作为包的入口点,简化模块加载
settings

模块加载机制

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)
lightbulb ?在路径中会被替换为模块名,支持灵活的模块组织

Lua最佳实践

Lua最佳实践

掌握Lua的最佳实践和技巧,能够编写出高效可维护优雅的代码。以下是一些在Lua 5.1编程中值得遵循的最佳实践和常用技巧。

speed

性能优化

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
lightbulb 局部变量访问速度比全局变量快,尽量使用局部变量
code

代码风格

良好的代码风格提高可读性和可维护性。

— 使用一致的命名规范
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
lightbulb 保持代码风格一致,使用有意义的变量和函数名
security

错误处理

良好的错误处理机制提高程序的健壮性。

— 使用assert进行前置条件检查
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
lightbulb 使用pcall和xpcall处理可能出错的代码,避免程序崩溃
architecture

设计模式

在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
lightbulb 利用Lua的表和元表特性,可以灵活实现各种设计模式

发表评论

Only people in my network can comment.
人生梦想 - 关注前沿的计算机技术 acejoy.com 🐾 步子哥の博客 🐾 背多分论坛 🐾 知差(chai)网 🐾 DeepracticeX 社区 🐾 老薛主机 🐾 智柴论坛 🐾