lua实例教程_lua教程网

lua实例教程_lua教程网目录Lua配置、安装、与运行Lua编译与调试环境搭建Lua基本语法1.交互式编程2.脚本式编程Lua中的数据类型1.数据类型展示2.注意事项Lua中的变量全局变量局部变量非局部变量(先看闭包)(upvalue)(实际代指变量而非值)(第一类函数)变量值的交换Lua中的闭包(词法定界)…待续(持续更新中)参考与引用Lua配置、安装、与运行1.进入Lua官网:http://www.lua.org——下载Lua2.下载文件【lua-5.3.4_Win64bin.zip】3.创建一个空文件夹“Lua

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新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账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号