【Lua】Lua语言学习
Lua相关概念
- 是轻量级可扩展的脚本语言。
- 语法比较简单类似C。
- 经常用来做游戏开发、嵌入式系统、数据分析、Web开发。
在线编译器
语法
代码块结构
- 使用do...end等关键字控制代码作用域,相当于大括号的开始和大括号结束。
- then只在条件语句中用。
变量与数据类型
- 用 local 定义一个局部变量,相当于js的let。
- 不加local的话则是定义一个全局变量,相当于js的var。
- 变量不用声明类型,不能以数字开头。
- 数据类型:nil空(null)、number数字(整数和浮点数)、string字符串、boolean布尔值、table表(类似数组)、function方法、userdata任意的C数据、thread独立线程。
- 不赋初始值的变量值为nil
- local a = 10; local b = "今天你吃了吗";local c = true;local d = {1,2,3};local e = function() return "吃了" end;
运算符
- 算数运算符(+、-、*、/、%、^);
- 关系运算符(==、不等于~=、<、>、<=、>=);
- 逻辑运算符 and(类似js的&&),or(类似js的||),not(类似js的!);
- 其他运算符 …(拼接字符串)、#(返回字符串或表的长度,相当于arr.length)
条件语句
if 、else if、else
- if 、elseif、else
- 写完if后面的条件之后要加 then
- 执行结束之后要加 end
- 0和空字符串在条件检查中视为true
a = 9
if (a + 1) > 10 then
print(a + 1)
else
print(a)
end
break
- 终止循环并将执行转移到紧接在循环或者switch后面的语句
循环语句
while、for、repeat-until
- while
- 写完执行条件后面要加 do
- 可用于写无限循环
- 可以使用break跳出
i = 0
while i < 10 do
print(i)
-- 不能写i++
i = i + 1
end
- for
- 写完执行条件后面要加 do
- for里的索引后面的数字默认是<=
- 例子里这个意思就是for(let i = 0; i <= 5; i++){}
- 执行结束之后要加 end
for i = 0, 5 do
print(i)
end
- repeat-until
- 先写循环内的操作,再写结束条件
- 执行结束不需要加 end
i = 0
repeat
print(i)
i = i + 1
until (i > 10)
-- 执行结束不需要加end
字符串
- 初始化时有三种形式
- 单引号引用 '字符串'
- 双引号引用 "字符串"
- 双中括号引用 [["字符串"]] 输出时保留双引号
- 常用转义字符:\n换行 \r回车 \反斜杠
- 常用字符串方法:
- string.gsub(org, find, replace) 返回用replace替换org中的find的字符串
- string.find(org, find, start, end) 返回org中find的起始索引和结束索引,如果未找到返回nil
- string.format() 返回格式化后字符串
- string.len(org) 返回org字符串的长度
string1 = "Lua"
string2 = "Tutorial"
number1 = 10
number2 = 20
print(string.format("字符串格式化 %s %s", string1, string2))
date = 2; month = 1; year = 2014
print(string.format("日期格式化 %02d/%02d/%03d", date, month, year))
print(string.format("%.4f",1/3))
函数
function 方法名(参数)
- 老东西,大体上没区别,记得最后写 end
function add(a,b)
return a + b
end
local a = 1
local b = 2
local c = add(a, b)
print(c)
- 可以使用...作为参数来创建具有可变参数的函数,简单来说就是传多少个参数都没问题(?),只要这些参数执行的都是同样的操作就不用在方法里定义很多个形参,看例子就懂了,但是有含义的形参不应该这么用吧
function average(...)
result = 0
local arg = {...}
for i,v in ipairs(arg) do
result = result + v
end
return result / #arg
end
print("The average is ",average(10,5,3,4,5,6))
表(Table)
local a = {1,2,3}
local b = {key = 1, value = '一'}
- 这是个类似将js的数组和对象结合起来的概念,既可以存键值对又可以存数组
- 如果是对象表的话键值对是用等号=连接不是冒号:
- 访问方法和数组与对象一样,但是数组下标从1开始:a[1] = 1 b.key = 1 b.value = '一'
- 表的遍历:
- 数组表遍历:for 索引 数组对象 in ipairs(数组表) do......end
- 对象表遍历:for 索引 键值对对象 in pairs(数组表) do......end
-- 数组表遍历
for i, v in ipairs(a) do
print(i, v)
end
-- 对象表遍历
for k, v in pairs(b) do
print(k, v)
end
- 常用表方法:
- table.concat(table, string, start, end) 表中字符串连接,其中string是连接字符(可选参数),start和end是起始索引和终止索引(可选参数)
- ex:myTable = {1,2,3} table.concat(myTable, "|", 2, 3)输出结果为2|3
- table.insert(table, index, value) 向指定位置添加值,其中index是要添加的下标(可选参数),value是要添加的值,如果没有下标默认在最后加
- table.remove(table, index) 删除指定位置的值,其中index是要删除的下标(可选参数),如果没有下标默认删除最后一个
- table.sort(table, comp) 表内排序,其中comp是比较器(可选参数),如果不填默认按字母表顺序
- table.concat(table, string, start, end) 表中字符串连接,其中string是连接字符(可选参数),start和end是起始索引和终止索引(可选参数)
元表
- 特殊的表,用于定义表的行为
- 可以通过原表实现运算符重载、索引和赋值的自定义行为
- 两种重要方法:
- setmetatable(table, metatable) 为表table设置元表metatable
- ex:mytable = setmetatable({}, {})
- getmetatable(table) 获取表的元表
- setmetatable(table, metatable) 为表table设置元表metatable
- 常用运算符行为相关元表方法:
- __add 更改 ‘+’ 运算符的行为。
- __sub 更改 ‘-’ 运算符的行为。
- __mul 更改 ‘*’ 运算符的行为。
- __div 更改 ‘/’ 运算符的行为。
- __mod 更改 ‘%’ 运算符的行为。
- __unm 更改一元 ‘-’ 运算符的行为。
- __concat 更改 ‘…’ 运算符的行为。
- __eq 更改 ‘==’ 运算符的行为。
- __lt 更改 ‘<’ 运算符的行为。
- __le 更改 ‘<=’ 运算符的行为。
- 其他方法:
- __index 元方法
- __newindex
- __call 用来添加方法调用的行为
- __tostring 用来修改打印语句的行为
-- 创建两个表
local a = {1, 2, 3}
local b = {4, 5, 6}
-- 创建一个元表
local metaTable = {}
-- 设置元表的加法运算符重载
metaTable.__add = function(t1, t2)
local result = {}
for i = 1, #t1 do
result[i] = t1[i] + t2[i]
end
return result
end
-- 将元表设置为表 a 和 b 的元表
setmetatable(a, metaTable)
setmetatable(b, metaTable)
-- 使用加法运算符
c = a + b
print(unpack(c)) -- 输出:5 7 9
- 当元表在表中不可用时,使用__index(元方法)查找元表
-- 为mytable设置了元表,其中包含__index 的函数,称之为元方法
mytable = setmetatable({key1 = "value1"},{__index = {key2 = "metatablevalue"}})
print(mytable.key1, mytable.key2)
- 将__newindex添加到元表中时,如果表中没有键,新键的行为将由元方法定义。当索引在主表中不可用时,可在元表中设置索引。如下示例:
mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })
print(mytable.key1)
mytable.newkey = "new value 2"
print(mytable.newkey,mymetatable.newkey)
mytable.key1 = "new value 1"
print(mytable.key1,mymetatable.newkey1)
--如果主表中存在键,则更新键。主表中没有键时,则将该键添加到元表中
- 用rawset函数更新同一表
- rawset设置值而不使用元表的__newindex。同样,rawget在不使用__index的情况下获得值。
mytable = setmetatable({key1 = "value1"}, {
__newindex = function(mytable, key, value)
rawset(mytable, key, "\""..value.."\"")
end
})
mytable.key1 = "new value"
mytable.key2 = 4
print(mytable.key1,mytable.key2)
- __call示例,返回主表中传递的值之和
mytable = setmetatable({10}, {
__call = function(mytable, newtable)
sum = 0
for i = 1, #mytable do
sum = sum + mytable[i]
end
for i = 1, #newtable do
sum = sum + newtable[i]
end
return sum
end
})
newtable = {10,20,30}
print(mytable(newtable))
- __tostring示例,修改打印值
mytable = setmetatable({ 10, 20, 30 }, {
__tostring = function(mytable)
sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "The sum of values in the table is " .. sum
end
})
print(mytable)
模块
- 创建一个表并将函数和变量存储在其中为一个模块
- 类似一个库
-- 一个模块有许多数学函数,称之为myModule,文件名是myModule.lua。文件内容如下
function myModule.add(a,b)
print(a+b)
end
function myModule.sub(a,b)
print(a-b)
end
function myModule.mul(a,b)
print(a*b)
end
function myModule.div(a,b)
print(a/b)
end
return myModule
- 在另一个文件中用require函数来加载模块
- 两个文件需要放在同一个目录中或将模块文件放在软件包路径中(需要额外设置)
- 模块名称及其文件名应相同
module = require('myModule')
module.add(10,20)
module.sub(30,20)
module.mul(10,20)
module.div(30,20)
协程
- 一个并行程序
- 使用resume函数调用协程时他开始执行,调用yield函数时停止执行
- 再次使用resume函数调用时从暂停的地方恢复执行直到协程执行完成
- 可用功能:
- coroutine.create(f) 创建一个带有函数f的协程,并返回一个类型为thread的对象
- coroutine.resume(co, 参数) 执行协程co并传递参数(可选参数),返回操作状态和可选的其他返回值
- coroutine.running() 返回正在运行的协程,如果为主线程则返回nil
- coroutine.status(co) 返回协程的状态:running、normal、suspended、dead
- coroutine.wrap(f) 类似create,但是返回一个函数,调用则恢复协程执行
- coroutine.yield(参数) 暂停正在运行的协程,如果传参数则参数作为附加值传递给resume
co = coroutine.create(function (value1, value2)
local tempvar3 = 10
print("协程步骤一", value1, value2, tempvar3)
local tempvar1 = coroutine.yield(value1+1, value2+1)
tempvar3 = tempvar3 + value1
print("协程步骤二", tempvar1, tempvar2, tempvar3)
local tempvar1, tempvar2 = coroutine.yield(value1+value2, value1-value2)
tempvar3 = tempvar3 + value1
print("协程步骤三", tempvar1, tempvar2, tempvar3)
return value2, "end"
end)
print("main", coroutine.resume(co, 3, 2))
print("main", coroutine.resume(co, 12, 14))
print("main", coroutine.resume(co, 5, 6))
print("main", coroutine.resume(co, 10, 20))
-- 说明一下执行顺序
-- 首先主程序为四个“main”的print,co为协程
-- 执行第一行主程序print("main", coroutine.resume(co, 3, 2))时开始执行协程,参数为3 2
-- 则协程中的value1 = 3,value2 = 2,这两个参数会保存在协程缓存中
-- 在协程中给tempvar3赋值10
-- 下一步执行协程中的协程步骤一的打印,此时value1 = 3,value2 = 2,tempvar3 = 10
-- 下一步执行协程的下一行,但是遇到了yield此时暂停协程
-- 协程暂停所以执行主程序的第一个main的打印,此时因为yield传了参数,所以第一个main中打印的协程的值为yield的参数,即打印为main true 4 3
-- 执行第二行主程序print("main", coroutine.resume(co, 12, 14))时再次开始执行协程,参数为12 14
-- 协程创建时第一次传递的参数为3 2,所以value1和value2不会因为协程再次启动传了新参数而发生变化
-- 因为tempvar1的赋值为暂停函数,所以它的值即为协程再次启动时传递的参数
-- 但是因为再次启动时传了2个值,而tempvar1只能接一个,所以tempvar1 = 12,tempvar2未被定义是nil
-- tempvar3是10 value1是3,tempvar3此时相加为13
-- 下一步执行协程中的协程步骤二的打印,此时tempvar1 = 12,tempvar2 = nil,tempvar3 = 13
-- 下一步执行协程的下一行,但是遇到了yield此时暂停协程
-- 协程暂停所以执行主程序的第二个main的打印,此时因为yield传了参数,所以第二个main中打印的协程的值为yield的参数,即打印为main true 5 1
-- 执行第三行主程序print("main", coroutine.resume(co, 5, 6))时再次开始执行协程,参数为5 6、
-- 因为tempvar1,tempvar2的赋值为暂停函数,所以它们的值即为协程再次启动时传递的参数
-- 因为再次启动时传了2个值,所以tempvar1,tempvar2都被赋值了,此时tempvar1 = 5,tempvar2 = 6
-- tempvar3是13 value1是3,tempvar3此时相加为16
-- 下一步执行协程中的协程步骤三的打印,此时tempvar1 = 5,tempvar2 = 6,tempvar3 = 16
-- 下一步执行协程的下一行,协程结束,返回值的value2 = 2和字符串end
-- 协程结束所以执行主程序的第三个打印,此时因为协程结束,所以返回协程的返回值 true 2 end,即打印为main true 2 end
-- 主程序的第三个打印结束,执行主程序第四行,此时协程已结束所以返回的是协程的返回值 false cannot resume dead coroutine,即打印为main false cannot resume dead coroutine
-- 主程序结束
常用
- -- 注释
- print() 打印
- type() 检查变量类型
- pcall(f) 返回错误状态
分类:
Lua相关
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?