lua实例教程_lua教程网

2022-11-01 16:31:38 浏览数 (1)

目录

  • Lua配置、安装、与运行
  • Lua编译与调试环境搭建
  • Lua基本语法
    • 1.交互式编程
    • 2.脚本式编程
  • Lua中的数据类型
    • 1.数据类型展示
    • 2.注意事项
  • Lua中的变量与函数
    • 全局变量
    • 局部变量
    • 非局部变量(先看闭包)
    • 变量值的交换
    • Lua中的函数
      • 函数原型
      • 函数中的常量与局部变量
      • 子函数原型
      • upvalue (实际代指变量而非值)
      • C闭包
      • Lua闭包
      • 关于闭包的理解(词法定界)
  • Lua处理从C#获得的数组、字典、结构体等
    • 方式1迭代器遍历以及转化为table后循环
    • 方式2类似C#的调用以及相关注意事项
  • …待续(持续更新中)
  • 参考与引用

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.复制代码到配置中

代码语言:javascript复制
    "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函数中可以内嵌函数,所以原型对象里面也有一个内嵌原型的列表,由此形成一个函数原型的树。

原型结构是这样的:

代码语言:javascript复制
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;

函数中的常量与局部变量

函数中的常量就是那些字面量,比如下面代码:

代码语言:javascript复制
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是一个局部变量的信息结构,主要用于调试的:

代码语言:javascript复制
//  本地变量的信息
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保存着内嵌函数的原型列表,比如下面的代码:

代码语言:javascript复制
function func()
local function sf1()
end
local function sf2()
end
end

sf1和sf2就是内嵌函数,所以func的函数原型就有两个子原型。

upvalue (实际代指变量而非值)

upvalue其实就是外部函数的局部变量,upvalues是这些upvalue的信息列表,Upvaldesc结构如下:

代码语言:javascript复制
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如果是上一层函数之外的局部变量,就像下面代码这样:

代码语言:javascript复制
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表达式时,会创建一个函数对象,其结构如下:

代码语言:javascript复制
typedef union Closure { 

CClosure c;
LClosure l;
} Closure;

正好对应了C闭包和Lua闭包,C闭包结构如下:

代码语言:javascript复制
// 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闭包结构如下:

代码语言:javascript复制
// 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长成这个样子,完全是因为它要解决作用域的问题。比如下面代码:

代码语言:javascript复制
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链表,原码如下:

代码语言:javascript复制
// 查找栈上的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代码:

代码语言:javascript复制
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关闭,代码如下:

代码语言:javascript复制
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状态:

关于闭包的理解(词法定界)

代码语言:javascript复制
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中闭包。

代码语言:javascript复制
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后循环

代码语言:javascript复制
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
代码语言:javascript复制
local t = obj.array:ToTable()
for i = 1, #t do
print("table:"..t[i])
end

方式2类似C#的调用以及相关注意事项

代码语言:javascript复制
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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/179564.html原文链接:https://javaforall.cn

0 人点赞