大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
目录
Lua配置、安装、与运行
1.进入Lua官网:http://www.lua.org——下载Lua
2.下载文件【 lua-5.3.4_Win64bin.zip 】
3.创建一个空文件夹“Lua”并在其中解压【 lua-5.3.4_Win64bin.zip 】压缩文件
4.复制“Lua”文件夹到一个不会被删除的地方,
5.路径最好不要是中文。( 推荐选择C盘 )
6.将此路径加入到环境变量中
7.cmd 中输入lua 查看安装是否成功
Lua编译与调试环境搭建
1.下载sublime
2.点击 菜单栏→→ 工具 →→ 编译系统 →→ 新编译系统
menu bar →→ Tools →→ Build System →→ new Build System
3.复制代码到配置中
"cmd": ["lua","$file"],
"file_regex":"^(...*?):([0-9]*):?([0-9]*)",
"selector":"source.lua"
3.保存到默认目录:命名为MyLua.sublime-build
注意:后缀一定要是 .sublime-build 。
保存,关闭Sbulime Text
4.重新打开Sbulime Text
点击 菜单栏→→ 工具 →→ MyLua(我们刚创建好的编译系统文件)
menu bar →→ Tools →→ MyLua
选中MyLua编译环境就可以运行了
5.保存完成→→点击“F7”或者“Ctrl+B”调试
Lua基本语法
1.交互式编程
Lua 提供了交互式编程模式。我们可以在命令行中输入程序并立即查看效果。
Lua 交互式编程模式可以通过命令 lua -i 或 lua 来启用:
2.脚本式编程
我们可以将 Lua 程序代码保存到一个以 lua 结尾的文件,并执行,该模式称为脚本式编程,如我们将如下代码存储在名为 hello.lua 的脚本文件中:
Lua中的数据类型
1.数据类型展示
print(type(“Hello world”)) –> string
print(type(10.4*3)) –> number
print(type(print)) –> function
print(type(type)) –> function
print(type(true)) –> boolean
print(type(nil)) –> nil
print(type(type(X))) –> string
2.注意事项
Lua中的变量与函数
全局变量
局部变量
非局部变量(先看闭包)
变量值的交换
Lua中的函数
函数原型
每个Lua函数都有一个原型,这是一个由GC管理的对象,它挂靠在函数上,为函数提供必要的信息,比如这个函数的操作码(opcodes),常量信息,本地变量信息,upvalue信息,和调试信息等等。
因为Lua函数中可以内嵌函数,所以原型对象里面也有一个内嵌原型的列表,由此形成一个函数原型的树。
原型结构是这样的:
typedef struct Proto {
CommonHeader;
// 固定参数的数量
lu_byte numparams; /* number of fixed parameters */
// 是否有可变参数
lu_byte is_vararg;
// 该函数需要的栈大小
lu_byte maxstacksize; /* number of registers needed by this function */
// upvalues数量
int sizeupvalues; /* size of 'upvalues' */
// 常量数量
int sizek; /* size of 'k' */
// 指令数量
int sizecode;
// 行信息数量
int sizelineinfo;
// 内嵌原型数量
int sizep; /* size of 'p' */
// 本地变量的数量
int sizelocvars;
// 函数进入的行
int linedefined; /* debug information */
// 函数返回的行
int lastlinedefined; /* debug information */
// 常量数量
TValue *k; /* constants used by the function */
// 指令数组
Instruction *code; /* opcodes */
// 内嵌函数原型
struct Proto **p; /* functions defined inside the function */
// 行信息
int *lineinfo; /* map from opcodes to source lines (debug information) */
// 本地变量信息
LocVar *locvars; /* information about local variables (debug information) */
// Upvalue信息
Upvaldesc *upvalues; /* upvalue information */
// 使用该原型创建的最后闭包(缓存)
struct LClosure *cache; /* last-created closure with this prototype */
// 源代码文件
TString *source; /* used for debug information */
// 灰对象列表,最后由g->gray串连起来
GCObject *gclist;
} Proto;
函数中的常量与局部变量
函数中的常量就是那些字面量,比如下面代码:
local function fun()
--ok true这些就是常量,Lua把所有的值都统一为TValue,常量也不例外,由TValue *k;保存。
local x = 1
local s = "ok"
local b = true
--而且常量只能是数字,布尔值,字符串,和nil这些基本类型,其他GC对象不可以是常量。由于常量不可变,所以直接保存在原型对象上就可以了。
end
函数中的固定参数,可变参数,和本地变量,都是局部变量,这些变量都存在函数关联的栈中,而栈中的元素就称为“寄存器”,maxstacksize指定该函数需要多少个寄存器,在创建Lua函数时就会在栈上预留这么多空间。因为可变参数的实际数量只有调用者才知道,所以maxstacksize不包含可变参数的数量。
locvars是一个局部变量的信息结构,主要用于调试的:
// 本地变量的信息
typedef struct LocVar {
// 本地变量名
TString *varname;
int startpc; /* first point where variable is active */
int endpc; /* first point where variable is dead */
} LocVar;
子函数原型
struct Proto **p保存着内嵌函数的原型列表,比如下面的代码:
function func()
local function sf1()
end
local function sf2()
end
end
sf1和sf2就是内嵌函数,所以func的函数原型就有两个子原型。
upvalue (实际代指变量而非值)
upvalue其实就是外部函数的局部变量,upvalues是这些upvalue的信息列表,Upvaldesc结构如下:
typedef struct Upvaldesc {
// 名字
TString *name; /* upvalue name (for debug information) */
lu_byte instack; /* whether it is in stack (register) */
lu_byte idx; /* index of upvalue (in stack or in outer function's list) */
} Upvaldesc;
instack指明这个upvalue会存在哪里,有两种情况要考虑:
uv如果是上一层函数的局部变量,且这个上层函数还在活动中,那么该局部变量一定还在上层函数的栈中。此时,instack为1,表明它在栈中,idx指定在栈中的索引,相对于上层函数的栈基址。
uv如果是上一层函数之外的局部变量,就像下面代码这样:
local x = 1
local function func()
local function innerfunc()
return x + 1
end
end
x在上两层函数之外声明,Lua是这样解决这个问题的:首先func会把x当成upvalue记录下来,然后innerfunc再从func的upvalue数组寻找。所以这种情况下,instack为0,则idx表示上层函数uv列表的索引。
实际的upvalue引用是在函数对象中的,这里只是一个描述信息,函数对象要根据这个信息才能引用到upvalue。
C闭包
Lua在执行到fucntion … end表达式时,会创建一个函数对象,其结构如下:
typedef union Closure {
CClosure c;
LClosure l;
} Closure;
正好对应了C闭包和Lua闭包,C闭包结构如下:
// nupvalues upvalue数量
// gclist为灰对象列表,最后由g->gray串连起来
#define ClosureHeader \
CommonHeader; lu_byte nupvalues; GCObject *gclist
// C闭包
typedef struct CClosure {
ClosureHeader;
lua_CFunction f; // C函数指针
TValue upvalue[1]; /* list of upvalues */ // update数组
} CClosure;
因为C函数相应简单,没有外层函数,所以upvalue其实就是保存在CClosure中的一个TValue数组。一个CClosure的实际大小通过sizeLclosure计算出,其内存布局如下:
| CClosure | TValue[0] | .. | TValue[nupvalues-1] |
因为CClosure的upvalue数组包含了一个元素,所以后面跟着的长度为nupvalues-1。通过luaF_newCclosure生成一个新的C闭包,实际应用中一般用lua_pushcclosure向栈顶压入一个新的C闭包,同时栈顶要装备好upvalue。函数实现如下:
// 生成一个C闭包并压入栈顶, n表示当前栈顶有多少个upvalue要与闭包关联
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
lua_lock(L);
if (n == 0) {
// 没有upvalue,它是轻量级C函数
setfvalue(L->top, fn);
api_incr_top(L);
}
else {
// 有upvalue,它是一个C闭包
CClosure *cl;
api_checknelems(L, n);
api_check(L, n <= MAXUPVAL, "upvalue index too large");
// 新建C闭包
cl = luaF_newCclosure(L, n);
cl->f = fn;
L->top -= n;
// 保存upvalue
while (n--) {
setobj2n(L, &cl->upvalue[n], L->top + n);
/* does not need barrier because closure is white */
}
setclCvalue(L, L->top, cl);
api_incr_top(L);
luaC_checkGC(L);
}
lua_unlock(L);
}
Lua闭包
Lua闭包结构如下:
// Lua闭包
typedef struct LClosure {
ClosureHeader;
struct Proto *p; // 函数原型
UpVal *upvals[1]; /* list of upvalues */ // upvalue列表
} LClosure;
通过sizeLclosure宏可获得Lua闭包的大小,其内存布局如下:
| LClosure | UpVal* | .. | UpVal* |
UpVal是对upvalue的间接引用,它的结构这样:
struct UpVal {
// 引用的值,该值可能在栈上(open),也可能是下面的TValue(close)
TValue *v; /* points to stack or to its own value */
// 引用计数
lu_mem refcount; /* reference counter */
union {
// 当v指向栈上时,open有用,next指向下一个,挂在L->openupval上
struct {
/* (when open) */
UpVal *next; /* linked list */
int touched; /* mark to avoid cycles with dead threads */
} open;
// 当v指向自己时,这个值就在这里
TValue value; /* the value (when closed) */
} u;
};
LClosure中记录的是UpVal指针,这说明一个UpVal可能会被多个Lua闭包引用,refcount就是这个引用计数。UpVal长成这个样子,完全是因为它要解决作用域的问题。比如下面代码:
function add (x)
return function (y)
return x+y
end
end
local add2 = add(2)
print(add2(5))
add函数调用完之后,参数x就超出作用域了,它本来在栈上,函数返回后它也会从栈中删除掉,但是add返回的函数对象还引用着这个x,这该怎么办呢?Lua是这样处理的。
UpVal有两种状态:
open状态 在这种情况下,其字段v指向的是栈中的值,换句话说它的外层函数还在活动中,因此那些外部的局部变量仍然活在栈上。
close状态 当外层函数返回时,就像上面代码那样,add2函数中的UpVal会变成关闭状态,即v字段指向自己的TValue,这样v就不依赖于外层局部变量了。
lua_State的openupval字段维护着一个open的链表,当创建一个Lua闭包时,调用luaF_findupval尝试从openupval链表中找到一个UpVal(根据函数原型的Upvaldesc信息),如果找得到就记录它并增加引用计数,如果找不到就创建一个新的UpVal,并加入openupval链表,原码如下:
// 查找栈上的uv。
UpVal *luaF_findupval (lua_State *L, StkId level) {
UpVal **pp = &L->openupval;
UpVal *p;
UpVal *uv;
lua_assert(isintwups(L) || L->openupval == NULL);
// 查找open的uv, open的uv由L->openupval串起来一个链表
while (*pp != NULL && (p = *pp)->v >= level) {
lua_assert(upisopen(p));
if (p->v == level) /* found a corresponding upvalue? */
return p; /* return it */
pp = &p->u.open.next;
}
/* not found: create a new upvalue */
// 如果未找到,创建一个新的加入链表
uv = luaM_new(L, UpVal);
uv->refcount = 0;
uv->u.open.next = *pp; /* link it to list of open upvalues */
uv->u.open.touched = 1;
*pp = uv;
uv->v = level; /* current value lives in the stack */
if (!isintwups(L)) {
/* thread not in list of threads with upvalues? */
L->twups = G(L)->twups; /* link it to the list */
G(L)->twups = L;
}
return uv;
}
比如下面这段Lua代码:
local x = 1
local y = 2
local z = 3
local function f1()
return x + 1
end
local function f2()
return x + 2
end
执行到f1声明时,创建一个Lua闭包,并创建一个UpVal挂到openupval链表上,接着执行到f2声明,此时从openupval可以到过UpVal,就直接引用它。
外层函数执行完毕的时候,会调用luaF_close将openupval中的一些UpVal关闭,代码如下:
vmcase(OP_RETURN) {
int b = GETARG_B(i);
if (cl->p->sizep > 0) luaF_close(L, base);
...
// 关闭栈中的upvalues,从level往后的upvalue,如果引用计数为0释放之,否则拷贝到UpVal自己身上
void luaF_close (lua_State *L, StkId level) {
UpVal *uv;
while (L->openupval != NULL && (uv = L->openupval)->v >= level) {
lua_assert(upisopen(uv));
L->openupval = uv->u.open.next; /* remove from 'open' list */
if (uv->refcount == 0) /* no references? */
luaM_free(L, uv); /* free upvalue */
else {
setobj(L, &uv->u.value, uv->v); /* move value to upvalue slot */
uv->v = &uv->u.value; /* now current value lives here */
luaC_upvalbarrier(L, uv);
}
}
}
luaF_close还会在其他地方执行,只要任何情况下留在栈中的局部变量被删除出栈,就会调这个函数。调完之后,UpVal本身就把局变量的值保存在自己身上了,这个过程对于函数是透明的,因为它总是间接的引用upvalue。
下图表示open和close的UpVal状态:
关于闭包的理解(词法定界)
function fn()
local i = 0
return function() -- 注意这里是返回函数的地址,不是执行
i = i + 1
return i
end
end
c1 = fn() -- 接收函数返回的地址
print(c1()) --> 1 --c1()才表示执行
--local i = 0的意思是重新创建一个新的变量,这里没有创建新的?
print(c1()) --> 2
--再次调用fn,将创建一个新的局部变量i
c2 = fn()
print(c2()) -->1
print(c1()) -->3
print(c2()) -->2
闭包在Lua中是一个非常重要的概念,闭包是由函数和与其相关的引用环境组合而成的实体。闭包=函数+引用环境。子函数可以使用父函数中的局部变量,这种行为叫做闭包。lua中函数是一种类型,可以被存放在变量或者数据结构中,可以当做参数传递给另一个函数,也可以是一个函数的返回值,也可以在运行期间被创建。Lua中还有一个非局部变量的概念,可以理解为不是在局部作用范围内定义的一个变量,同时,它又不是一个全局变量,也就是大家说的upvalue。这种变量主要应用在嵌套函数和匿名函数中(这个变量的环境就是前面说的引用环境)。在Lua函数中再定义函数,称为内嵌函数,内嵌函数可以访问外部函数已经创建的所有局部变量,而这些变量就被称为该内嵌函数的upvalue(upvalue实际指的是变量而不是值),这些变量可以在内部函数之间共享。于是成全了Lua中闭包。
function Closure()
local ival = 10 --upvalue
function InnerFun1() --内嵌函数
print(ival)
end
function InnerFun2()
print("Before",ival)
ival = ival + 10
print("After", ival)
end
return InnerFun1, InnerFun2
end
--将函数赋值给变量,此时变量a绑定了函数InnerFun1,b绑定了函数InnerFun2
local a, b = Closure()
--调用a
a()
--调用b
b()
Lua处理从C#获得的数组、字典、结构体等
方式1迭代器遍历以及转化为table后循环
local testData = CS.LuaCallCSUtils.GetTestData() --为一个字典
local iter = testData:GetEnumerator()
local list = {
}
while iter:MoveNext() do
--数组或list直接获取Current
--local v = iter.Current
local k = iter.Current.Key
local v = iter.Current.Value
list[k] = v --转为table
print(k, v)
end
local t = obj.array:ToTable()
for i = 1, #t do
print("table:"..t[i])
end
方式2类似C#的调用以及相关注意事项
print("*****************Lua调用C#数组******************");
local Lesson3=CS.Lesson3();
--Lua使用C#数组相关知识
--长度 userdata
--C#怎么用 lua就怎么用
print(Lesson3.testArray.Length);
--访问元素
print(Lesson3.testArray[0]);
--遍历要注意 虽然lua中索引从1开始
--但是数组是C#那不得规则 所以 还是要按照C#来
--注意最大值 一定要减1 lua中是可以取到最后一个值得 nil
for i=0,Lesson3.testArray.Length-1 do
print(Lesson3.testArray[i]);
end
--Lua创建一个C#得数组 lua中表示数组和List可以用表
--但是创建C#中的数组,使用Array类中的静态方法即可
local Array2 = CS.System.Array.CreateInstance(typeof(CS.System.Int32),10);
print(Array2.Length);
print("修改前"..Array2[0]);
Array2[0]=100;
print("修改后"..Array2[0]);
print("*****************Lua调用C# List******************");
Lesson3.testList:Add(1);
Lesson3.testList:Add(2);
--长度
print(Lesson3.testList.Count);
--遍历
for i=1,Lesson3.testList.Count-1 do
print(Lesson3.testList[i])
end
print(Lesson3.testList);
--Lua创建一个List对象
--老版本
local list2 = CS.System.Collections.Generic["List`1[System.String]"](); print(list2); list2:Add("老版本创建List"); print(list2[0]); --新版本>V2.1.12 local List_StringTemp = CS.System.Collections.Generic.List(CS.System.String); local list3 = List_StringTemp(); list3:Add("新版本创建List"); print(list3[0]); print("*****************Lua调用C#数组、List、字典相关知识点******************");
Lesson3.testDic:Add(1,"123");
print(Lesson3.testDic[1]);
--遍历
for k,v in pairs(Lesson3.testDic) do
print(k,v)
end
--Lua中创建一个字典对象(新版本)
local Dic_String_Vectory3 = CS.System.Collections.Generic.Dictionary(CS.System.String,CS.UnityEngine.Vector3);
local dic2 = Dic_String_Vectory3();
dic2:Add("123",CS.UnityEngine.Vector3.right);
--lua中创建的字典,key是string时,通过中括号是获取不到值的
print(dic2["123"]);--nil
--使用TryGetValue 两个返回值 第一个返回值为是否获取到 第二个为获取到的值
print(dic2:TryGetValue("123"));
--如果要通过健来获取值 通过固定方法
print(dic2:get_Item("123"));
--修改也是固定
dic2:set_Item("123",nil);
print(dic2:get_Item("123"));
…待续(持续更新中)
参考与引用
https://www.runoob.com/lua/lua-tutorial.html
https://blog.csdn.net/ChinarCSDN/article/details/78667262
https://blog.csdn.net/Yeyushenfan/article/details/83039573
https://zhuanlan.zhihu.com/p/98917625
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/179564.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...