Lua
niuxingxing.feishu.cn/docs/doccnp…
注释
单行注释 --
多行 --[[ ]]--
String、function、userdata、thread、table可以被垃圾回收器进行回收,nil、boolean、number不被垃圾回收所管理
数据类型
nil
不存在的引用默认为nil,nil进行比较需要加上“”,type()类型返回为String
boolean
false和nil 为 false
其他所有和数字 0 为 true,
number
string
- 所有字符串做运算时会自动进行转换为数字
- 使用..进行字符串拼接
- #输出string的长度
字符串操作
string.upper(x) -- 全部转换为大写
string.lower(x) -- 小写
-- 字符串替换 replace方法
-- s原始字符串 ,target要替换的内容,r替换成的,num默认全部替换
-- return s字符串的copy,替换的总次数
string.gsub(s, target, r, num)
--字符串反转
string.reverse(arg)
-- 格式化输出
string.format("val is :%d", 4) -- val is 4
string.format("val is :%f", 4) -- val is 4.000000
-- 长度
string.len("abc")
-- findAll 方法
-- 返回一个迭代器,每一个和pattern匹配的字符
string.gmatch(str, pattern)
-- find
-- str源字符,pattern正则表达式,起始位置
string.match(str, pattern)
string.match(str, "%a+") --按照空格分隔字符串
-- substring
local substring = string.sub(sourcestr, start, end)
table
niuxingxing.feishu.cn/docs/doccng… 放牛星星表总结
表( Table )是 Lua语言中最主要(事实上也是唯一的)和强大的数据结构 。
使用表, Lua语言可以以一种简单、统一且高效的方式表示数组、集合、记录和其他很多数据结构 。 Lua语言也使用表来表示包( package)和其他对象。 当调用函数 math.sin 时,我们可能认为是“调用了 math 库中函数 sin ”;而对于 Lua语言来说,其实际含义是“以字符串sin为key检索表 math”。
local t = {}
t = {"banana","orange","apple"}
默认开始索引为1
table遍历
1. ipairs
2. pairs
ipairs 遇到nil就停止
-- concate()将table中元素进行拼接
table.concat(t) -- bananaorangeapple
table.concat(t, ",") -- banana, orange, apple
table.concat(t,", ", 2,3)) -- orange, apple
-- insert(), 插入元素
table.insert(t, "mango") -- 尾部插入
table.insert(t, index, "mango") -- 指定位置插入
table.remove(t) -- 移除最后一个
-- sort()
-- 默认从小到大排序
local test0 ={1,9,2,8,3,7,4,6}
table.sort(test0, function(a, b) return a > b end) -- 从大到小
-- 使用#会在索引中断处停止计算,计算table大小工具
function getLength(t)
local len = 0
for k, v in pairs(t) do
len = len + 1
print(k .. " " ..v)
end
return len
end
Lua table默认API不提供深拷贝
---克隆对象(建议用于克隆Class对象)
---@param any 对象
---@return any 克隆对象
function Clone(object)
local lookup_table = {}
local function _copy(object)
if type(object) ~= "table" then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for key, value in pairs(object) do
new_table[_copy(key)] = _copy(value)
end
return setmetatable(new_table, getmetatable(object))
end
return _copy(object)
end
function 函数
function testFun(a, b)
return a + b
end
-- 可变参数
function average(...)
result = 0
local arg={...} --> arg 为一个表,局部变量
for i,v in ipairs(arg) do
result = result + v
end
print("总共传入 " .. #arg .. " 个数")
return result/#arg
end
--返回可变参数长度
select('#', ...)
可变长参数
-- ...
function foo(...)
local params = {...} or {}
local count = 0
for k, v in ipairs(params) do
print(k, v)
count = count + 1
end
print(string.format("count is %d", count))
end
foo(2, 3, 1, 55, print, "s")
--1 2
--2 3
--3 1
--4 55
--5 function: 00D84C70
--6 s
-- count is 6
-
upValue上值,上值就是闭包中的局部变量。内嵌函数可以访问外包函数中创建的局部变量。
-
闭包Clourse,闭包是一个动态生成的数据对象,包含了函数原型的引用、全局表的引用和一个数组(包、Upvalue)。 虚拟机栈会创建一个closure对象包并且包含了function中所有的变量进入栈。每次调用function的时候都可以读取到变量
Continue.................................
循环
- While循环
- for循环
- repeat until (do while)
Conditional
if(布尔表达式)
then
--[ 在布尔表达式为 true 时执行的语句 --]
end
if(布尔表达式)
then
--[ 布尔表达式为 true 时执行该语句块 --]
else
--[ 布尔表达式为 false 时执行该语句块 --]
end
--[ 定义变量 --]
a = 100;
b = 200;
--[ 检查条件 --]
if( a == 100 )
then
--[ if 条件为 true 时执行以下 if 条件判断 --]
if( b == 200 )
then
--[ if 条件为 true 时执行该语句块 --]
print("a 的值为 100 b 的值为 200" );
end
end
goto
用于跳转到指定 ::tag:: 处
goto room1 -- initial room
::room1::
do
local move = io.read()
if move == "south" then
goto room3
elseif
move == "east" then
goto room2
else
print("invalid move")
goto room1 -- stay in the same room
end
end
::room2::
do
local move = io.read()
if move == "south" then
goto room4
elseif
move == "wast" then
goto room1
else
print("invalid move")
goto room2 -- stay in the same room
end
end
::room3::
do
local move = io.read()
if move == " north" then
goto room1
elseif
move == "east" then
goto room4
else
print("invalid move")
goto room3 -- stay in the same room
end
end
::room4::
do
print("Congratulations, you won!")
end
元表
元表用来定义一个表的基本行为。 多个表和userdata可以共享同一个表。
所有的number共同引用一个元表。
所有的string共享同一个元表
Metatable
setmetatable(table,metatable): 对指定 table 设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。
getmetatable(table): 返回对象的元表(metatable)。
-- rawget方法获取table[index]的真实值, 不会查找元表的__index中的 value
rawget(table, index)
-- 下面这段代码含义
setmetatable(train_set,
{
__index = function(t, i)
return {t.data[i], t.label[i]}
end
})
等价于:
local my_metatable = {
__index = function(t, i)
return {t.data[i], t.label[i]}
end
}
setmetatable(train_set, my_metatable)
-- 当去访问train_set[4] 并且 key 4为nil不存在表中的时候
-- __index = function(train_set, 4)
__index
使用table根据key去获取value,如果表中没有指定key或者table的类型不是table,就去查找元表的__index。__index的值可以为函数或者表
__newIndex
对不存在的key进行赋值的时候,就会触发__newindex方法
t ={}
local _t = t
t = {}
local mt = {
-- get
__index = function (t, k)
print("Access to element" .. tostring(k))
return _t[k]
end,
-- set
__newindex = function(t, k, v)
print("Successful set element \"" .. tostring(k) .. "\" as" .. tostring(v))
_t[k] = v
end
}
setmetatable(t, mt)
t[2] = "new"
print(t[2]
--[[
Successful set element "2" asnew
Access to element2
new
--]]
__call
调用func()当func类型不是function的时候,__call触发
-- 当元表中设置了__call之后就可以把table a当作function()去使用
local a = setmetatable({}, __call = function(self, index)
error(index)
end
a(1) -- 1
__tostring
定义表的输出形式,print(table)的默认形式
Lua模块与包
作用域 SCOPE
Lua语言中的变量在默认情况下是全局变量,所有的局部变量在使用前必须声明 。 与全局变量不同,局部变量的生效范围仅限于声明它的代码块。一个代码块( block)是一个控制结构的主体,或是一个函数的主体,或是一个代码段(即变量被声明时所在的文件或字符串
- 局部变量可以避免由于不必要的命名而造成全局变量的混乱
- 局部变量还能避免同一程序中不同代码部分中的命名冲突
- 访问局部变量比访问全局变量更快
- 局部变量会随着其作用域的结束而消失,从而使得垃圾收集器能够将其释放。
声明为local的变量仅在当前作用域下存在,不会被保存在_G表中
模块化加载module
每个模块可以是一个table结构,包含成员变量和成员方法
-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}
-- 定义一个常量
module.constant = "这是一个常量"
-- 定义一个函数
function module.func1()
io.write("这是一个公有函数!\n")
end
local function func2()
print("这是一个私有函数!")
end
function module.func3()
func2()
end
return module
-- 使用require进行加载模块
local module = require("module")
module.func1()
加载使用.so 动态链接库
local path = "xxxxx"
local f, error = loadlib(path, "function_name")
f()
Lua面向对象
I/O 输入输出
简单模型
当前输入和当前输出流
io.write 相比较 print为完全可控制的输出,不会在最终的输出结果中添加诸如制表符或换行符这样的额外内容。此外,函数 io.write 允许对输出进行重定向,而函数 print 只能使用标准输出 。
print()可以自动为参数调用tostring
io.write在将数值转换为字符串时遵循一般的转换规则。可以使用string.format
io.input(filename) --以只读模式打开指定文件,并将文件设置为当前输入流。
io.write(a, b, c)
io.write(string.format("sin(3) = %.4f\n", math.sin(3))) --> sin(3) = 0.1411
io.read()
函数 io.read 可以从当前输入流中读取字符串,其参数决定了要读取的数据:
-- 对文件进行排序
local lines = {}
for line in io.lines() do
lines[#lines + 1] = line
end
table.sort(lines)
-- write all the lines
for _, l in ipairs(lines) do
io.write(l, "\n")
end
io.read(0)是一个特例,它常用于测试是否到达了文件末尾 。 如果仍然有数据可供读取,它会返回一个空字符串;否则,则返回 nil。
完全模式
io.open(),第一个参数是要打开文件的文件名,第二个参数为io mode。
r只读,w只写,a追加,b代表二进制
local f = assert(io.open(filename, "r"))
local t = f:read("a")
f:close()
Error Handling异常处理
- 使用error()错误处理: 没有返回值地抛出异常方法
- 使用assert()进行错误处理
- pcall()return 一个状态码,pcall是一种安全的调用方式,当发生Exception的时候就会中断程序执行pcall指定的handler
使用error抛出异常,使用pcall捕获异常
Stack 虚拟机和栈 及相关数据结构
相关内容可参考第2、3章 github.com/lichuang/Lu…
Lua使用基于寄存器的虚拟机,虚拟机存储OPCODE,解释器将字节转换成虚拟机指令,存储到intructions,虚拟机读取指令生成机器码交由CPU执行。
Lua通过虚拟机栈和C进行参数传递,当Lua CallC function的时候会创建一个独立虚拟机栈保存所有函数调用所需要的数据。 虚拟机中的Stack作为一个中间中转,C Call Lua可以让Lua从栈中读取数据。相反,Lua也可以封装为数据结构放入栈中供C读取。来实现双向通信。
流程关系: LuaState -> 解析lua脚本.lua -> proto(function)-> Closure -> 推入Stack -> 生成CallInfo函数调用的结构体 -> LClourse指针 -> 执行特定Lua OPCODE
Closure : lua运行时的函数实例对象 proto :lua内部代表一个cloure原型的对象,有关函数部分信息都保存在这里
closure是运行期的对象,与运行期关系更大;而与编译期相关的其实是proto对象,他才是编译过程真正需要生成的目标对象。
调用lua_load api,将一个chunk进行编译。lua_load根据当前chunk生成一个mainfunc proto,然后为这个proto创建一个closure放到当前的栈顶,等待接下来的执行。Chunk内部的每个function statement也都会生成一个对应的proto,保存在外层函数的子函数列表中。所有最外层的function statement的proto会被保存到mainfunc proto的子函数列表中。所以,整个编译过程会生成一个以mainfunc为根节点的proto树
CallInfo
LuaInit
Garbage Collection垃圾回收
string、table、function、userdata、thread 五种数据类型可以被垃圾回收。