Lua简介
轻量小巧、脚本语言、C语言开发、开源、可拓展
设计目的:为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能
快速入门
参考资料
核心内容精简
- 注释
--
--[[]]--
- 数据类型
nil/number/boolean/string/function/userdata/thread/table
- 全局变量、局部变量local、表中的域
- 循环
while do end
for i=1,10,1 do end
for i,v in ipairs(a) do end
repeat until
break
goto
- 条件
if then elseif then else end
(只有nil和false为假) - 函数 以end结束,无{} 两种定义、多返回值、可变参数
arg = {...}
- 特殊的运算符
~=
and or not
连接字符串..
返回串或表长度#
- 字符串
‘’
“”
[[]]
转义字符、string类包含字符串操作 - 数组,定义
array={}
取值赋值array[x]=x
索引从1开始 - 迭代器,泛型for 无状态 多状态
- 表table,可以实现数组、哈希表、对象、集合、键的类型自由度高、table类下包含表的操作
- 模块,其实就是拥有自定义属性和方法的表、定义模块
return 表名
、加载模块require("表名")
、需添加加载路径 - 元表metatable,自定义操作方法、设置元表
setmetatable(表,元表)
、返回元表getmetatable(表)
,元表中要包含元方法,没有元方法的元表是无意义的
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
-
元方法,
__index
若是表,则相当于扩展了原表的属性和方法,若是函数,则元表查不到的话就执行该函数__newindex
,如上,只不过是针对对表中不存在的索引赋值时调用__call
,调用值时用的,如table(xxx)__tostring
,输出用的,如print(table)其他就是一些运算与比较操作了
-
协同程序coroutine,四种状态,用来解决生产者-消费者问题
-
错误处理,
assert(判断语句,报错信息)
、中止正在执行的函数error(报错信息)
-
垃圾回收,系统自动,赋值nil为手动
-
面向对象,
封装:定义属性
table={名=值,名=值...}
、定义方法function table:名(xxx) end
、创建对象(前提有一个生成对象的方法)调用方法即可、访问属性o.属性
、访问方法o:方法()
继承:派生类 = 基类构造的对象,再重写new方法(创建对象的方法)
派生类重写基础类的函数:再定义一次函数就行
-- Meta class
Shape = {area = 0}
-- 基础类方法 new
function Shape:new (o,side)
o = o or {}
setmetatable(o, self)
self.__index = self
side = side or 0
self.area = side*side;
return o
end
-- 基础类方法 printArea
function Shape:printArea ()
print("面积为 ",self.area)
end
-- 创建对象
myshape = Shape:new(nil,10)
myshape:printArea()
Square = Shape:new()
-- 派生类方法 new
function Square:new (o,side)
o = o or Shape:new(o,side)
setmetatable(o, self)
self.__index = self
return o
end
-- 派生类方法 printArea
function Square:printArea ()
print("正方形面积为 ",self.area)
end
-- 创建对象
mysquare = Square:new(nil,10)
mysquare:printArea()
Rectangle = Shape:new()
-- 派生类方法 new
function Rectangle:new (o,length,breadth)
o = o or Shape:new(o)
setmetatable(o, self)
self.__index = self
self.area = length * breadth
return o
end
-- 派生类方法 printArea
function Rectangle:printArea ()
print("矩形面积为 ",self.area)
end
-- 创建对象
myrectangle = Rectangle:new(nil,10,20)
myrectangle:printArea()
XLua
参考资料
核心内容精简
-
文件加载
执行字符串
luaenv.DoString("print('hello world')")
加载Lua文件
DoString("require 'byfile'")
自定义Loader???
-
C#访问Lua
获取全局基本数据类型
luaenv.Global.Get<数据类型>("a")
其他 同样用Get方法,但要映射一下
-
Lua访问C#
lua里头没有new关键字;
所有C#相关的都放到CS下,包括构造函数,静态成员属性、方法;
Intellij IDEA
常用的编辑Lua的工具
常用快捷键
- 查询一个方法在哪些地方被调用:
ctrl
+点击方法 - 撤销、回退:
ctrl+z
ctrl+shift+z
- 当前文本查找:
ctrl+f
- 所有文本查找:
ctrl+shift+r
- 当前文本替换:
ctrl+r
- 递进式选择代码:
ctrl+w
- 最近打开的文件记录:
ctrl+e
- 查询类名:
ctrl+n
- 查询所有:右上角搜索符号
- 方法参数提示:
ctrl+p
过滤指定的后缀文件
File>Settings>Editor>File Types
最下方输入,注意星号和分号
显示未保存星号
File>Settings>Editor>General>Editor Tabs
遇到的坑
函数定义和调用的两种形式
1. 点号
shape = {side = 4}
function shape.set_side(shape, side)
shape.side = side
end
function shape.print_area(shape)
print(shape.side * shape.side)
end
print(shape.side)
shape.set_side(shape, 5)
print(shape.side)
shape.print_area(shape)
输出
4
5
25
2. 冒号
冒号其实和点号类似,只是把第一个隐藏参数省略了,而 self 则是指向调用者自身
shape = {side = 4}
function shape:set_side(side)
self.side = side
end
function shape:print_area()
print(self.side * self.side)
end
print(shape.side)
shape:set_side(5)
print(shape.side)
shape:print_area()
输出
4
5
25
更深入
可以用点号“ . ”来定义函数,冒号“ :”来调用函数
可以用冒号“ :”来定义函数,点号“ . ”来调用函数
shape = {side = 4}
function shape.set_side(shape, side)
shape.side = side
end
function shape.print_area(shape)
print(shape.side * shape.side)
end
print(shape.side)
shape:set_side(5)
print(shape.side)
shape:print_area()
shape = {side = 4}
function shape:set_side(side)
self.side = side
end
function shape:print_area()
print(self.side * self.side)
end
print(shape.side)
shape.set_side(shape, 5)
print(shape.side)
shape.print_area(shape)
self是个啥
把上面的函数的定义和调用的方式看懂了就知道了
就是用冒号定义函数后,self表示调用者
实现private和public
Lua中没有private和public,继承的表中的所有属性和方法都可访问
实现:
function GetPerson()
-- 要封装的类
local Person =
{
ID= 0, -- ID
name= "", -- 名字
}
-------------------------- 封装的操作 ---------------------------------
-- 设置ID
local function SetID(ID)
Person.ID = ID
end
-- 获取ID
local function GetID()
return Person.ID
end
-- 设置名字
local function SetName(name)
Person.name = name
end
-- 获取名字
local function GetName()
return Person.name
end
-- 对外提供的接口
-- 原理:新建一个临时表,外面只能操作这里定义的操作
return {SetID = SetID, GetID = GetID, SetName = SetName, GetName = GetName}
end
------------------------------ 测试代码 ------------------------------------
local person = GetPerson()
person.SetID(100)
print(person.GetID())
person.SetName("小明")
print(person.GetName())
神奇的“变量提升”
定义变量,但未赋值,接着就作为函数参数直接使用,到最后才给变量赋值予函数,这样竟然还能成功执行
严格来说不叫变量提升,而是脚本语言的特点
脚本语言:
- 无需编译,程序代码就是最终的可执行程序
- 由所在的解释器负责解释,或者手动编写一个程序可以解释后缀为.aa的文件,当对.aa的文件内容进行重写时,仍用上述程序进行解释,这就叫脚本语言
- 脚本语言其实就是解释性语言,翻译一行然后就执行一行,哪怕后面有些内容是有错的也不影响前面语句的执行.非脚本语言就是一定要全部都没错,然后编译通过了然后才能运行
释惑:
因为Lua中的变量都是引用类型(这个从视频中看的,待考究),而当作为函数参数使用时它是以变量名的形式传入,真正要取得其中值的时候,已经被赋值了.....
(感觉解释不清楚,参考以下js的吧)
let target = {};
let source = { a: { b: 2 } };
Object.assign(target, source);
console.log(target); // { a: { b: 10 } };
source.a.b = 10;
console.log(source); // { a: { b: 10 } };
console.log(target); // { a: { b: 10 } };
注意,第一次打印的结果target竟然是操作后的值
因为target中含有引用元素,则target和sourse还有联系
而console.log是从内存中查值,改变sourse后内存中两者共有的这个对象元素{b:2}都改变了
通俗地讲:
控制台一开始显示的是{a: {...}}
展开的这个过程是从内存查值的过程,不是相当于照相(固定一瞬间的状态)
深入模块载入
模块载入,立即执行(若重复载入同一模块,只执行第一次):requeire(“model_name”)
模块载入,每次都执行:dofile(“my.lua”)
载入时不执行,需要时才执行:
local my=loadfile(“my.lua”)
...
my()
深扒模块载入原理:
fp=require(“my”)
-------等价于-------
fp= (function()
--my.lua文件内容--
end)
所以模块定义时可以有返回值,也可以没有
没有返回值那加载模块时就是直接执行整个文件,若有返回值,不仅执行整个文件,还能定义变量拿到该返回值,再次使用该返回值(或其中函数)