Lua程序设计
-
程序段:我们将Lua语言执行的每一段代码(例如,一个文件或交互模式下的一行)称为一个程序段(Chunk),即一组命令或表达式组成的序列。
-
一些词法规范:下划线+大写 是特定变量命名规范
-
全局变量
-
Boolean类型:逻辑运算符 and or not;利用短路原则 a = x or 1;
a > b and a or b,相当于C语言中 a >b ?a: b; not 运算符永远返回true or false -
独立解释器:是一个可以直接使用Lua语言的小程序。
-
全局变量arg:编译器在运行代码前会通过预先定义的全局变量arg来获取解释器传入的参数。其中索引0中保存的内容为脚本名
-
数值
-
闭包:在Lua中函数可以被保存到变量(全局或局部)中,也可以作为某个参数传递给其他函数,也可以作为函数返回值——“第一类值”;Lua中函数可以访问包含其自身的外部函数中的变量——“词法定界”
一个闭包就是一个函数外加能够使该函数正确访问非局部变量所需的其他机制。借助闭包我们可以重定义一个函数,或者是重新封装,构建一个沙盒。
- 编译:loadfile,从文件中加载Lua代码段,编译代码,将编译后的代码段作为一个函数返回
注意:解释型语言的区分并不在于源码是否被编译,而在于是否有能力(且轻易地)执行动态生成的代码。
-
模块:创建模块的方式,一种是创建一个表并将所有需要导出的函数放入其中,最后返回这个表。另一种是把所有函数定义为局部变量,然后在最后构造返回的表。
-
元表和元方法
-
面向对象:参考基于原型的语言中的一些做法在lua中模拟类。我们只需创建一个专门被用以其他对象(类的实例)的原型对象即可。类和原型都是一种组织多个对象间共享行为的方式。
-
垃圾回收:标记—清除机制。C 和 Lua 通过一个栈来进行交互,而且栈是属于Lua状态的一部分,所有垃圾收集器是知道C语言正在使用哪些值的。
-
Lua解释器: 一个可执行Lua的可执行文件,是一个小应用,它是由Lua标准库实现的独立解释器。这个解释器负责与用户的交互,将用户的文件和字符串传递给Lua标准库,由标准库完成主要的工作(例如,真正地运行代码)
-
在Lua中调用C语言:我们必须以一种恰当的方式为Lua提供该C函数的地址,也就是要注册该函数。Lua调用C函数时,也使用了一个与C语言调用Lua函数时相同类型的栈,C函数从栈中获取参数,并将结果压入栈。
-
用户数据(userdata):Lua中提供的可以用来存储任何数据的原始内存区域。
-
Lua状态:在支持多线程的系统中,一种有趣的设计是为每个线程创建一个独立的Lua状态。这种设计使得线程类似于POSIX进程,它实现了非共享内存的并发。
Lua复习总结
1.Lua中深拷贝和浅拷贝的区别?如何实现深拷贝
深拷贝就是开辟了一块新的内存,内存中存的是复制后的值
浅拷贝就是创建了一个指针,指向了共同的内存
浅拷贝只是简单的赋值,对新表的修改会影响原始的表;
深拷贝就是完全复制一份全新的表出来,和原始的表之间互不影响。实现方式:迭代遍历要复制的表的所有元素,最后还要拷贝元表(就是讲原来表的元表设置为新表的元表)。
拷贝:指的是用已经存在的对象创建出一个新的对象。从本质上讲,对象也是一份数据,因为它会占用内存。
创建对象:严格来说,包括两个阶段,首先要分配内存空间,然后再进行初始化。拷贝是在初始化阶段进行的,也就是用其他对象的数据来初始化新对象的内存。
浅拷贝:对于基本类型的数据以及简单的对象,他们之间的拷贝非常简单,就是按位复制内存,这种默认的拷贝行为就是浅拷贝。
深拷贝:当类持有其他资源,如动态分配的内存,指向其他数据的指针等,默认的拷贝构造函数就不能拷贝这些资源了,我们必须显示地定义拷贝构造函数,以完整地拷贝对象的所有数据。这种将对象所持有的其他资源一并拷贝的行为叫做深拷贝,我们必须显示地定义拷贝构造函数才能达到深拷贝的目的。不进行深拷贝的话,拷贝出来的对象如果包含指针的话会和原对象指针指向同一块内存,这样互相之间就会相互影响 ,这时就需要把原对象指针指向的那块内存也拷贝一份,新对象的指针指向新的内存。
C# 深拷贝:为什么要用到深拷贝?比如我们建了某个类Person,并且实例化出一个对象,然后,我们需要把这个对象复制一遍,并且复制出来的对象要跟之前的一模一样,我们一般会如何处理呢?
public class CopyTest
{
public static void CopyPerson()
{
Person p = new Person();
p.Name = "wang";
p.Num = 19;
Person p2 = p;
p2.Name = "jun";
p2.Num = 29;
System.Console.WriteLine(p.Name + "," + p.Num.ToString());
System.Console.WriteLine(p2.Name + "," + p2.Num.ToString());
}
}
class Person
{
public string? Name { get; set; }
public int Num { get; set; }
}
运行后发现,新复制的对象的修改影响到了原对象。这里的原理是因为"=",在对引用类型使用时,仅仅是新建了一个新的引用变量,然后把引用地址复制给了新的引用变量而已,并没有复制真正地内容,这时候如果需要复制真正内容的话,就需要用到深拷贝的方式了。具体实现可以考虑反射和序列化。
public static T DeepCopy<T>(T obj)
{
if (obj == null)
{
return obj;
}
var type = obj.GetType();
if (obj is string || type.IsValueType)
{
return obj;
}
var retVal = Activator.CreateInstance(type);
FieldInfo[] fieldInfos = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
foreach (var field in fieldInfos)
{
try
{
field.SetValue(retVal, DeepCopy(field.GetValue(obj)));
}
catch (System.Exception)
{
// throw;
}
}
return (T)retVal;
}
public static T DeepCopyByXml<T>(T obj)
{
if (obj == null)
{
return obj;
}
object retVal;
using (MemoryStream ms = new MemoryStream())
{
XmlSerializer bf = new XmlSerializer (typeof(T));
bf.Serialize(ms, obj);
ms.Seek(0, SeekOrigin.Begin);
retVal = bf.Deserialize(ms);
ms.Close();
}
return (T)retVal;
}
回到Lua中,深拷贝的实现方式,遍历要拷贝的表,最后不要忘了原表也要拷贝
local a = {}
a.x = 1
a.b = {}
a.b.c = 2
function deepCopy(originObj)
local _copy
function _copy(obj)
if type(obj) ~= "table" then
return obj
end
local newTable = {}
for key, value in pairs(obj) do
newTable[_copy(key)] = _copy(value)
end
return setmetatable(newTable, getmetatable(obj))
end
return _copy(originObj)
end
local m =deepCopy(a)
m.b.c =3
print(a.b.c)
2.lua中ipairs和pairs的区别?
对于复杂的表,ipairs 是顺序遍历,遇到nil会停止,pairs是随机遍历,能遍历整个表。
3.lua中的userdata是什么?有什么作用?
是名为用户数据的基本类型。比如C语言和Lua交互的时候,在lua中可以用userdata 表示C中的任意类型数据,作用是提供了用来存储任何数据的原始内存区域。
4.解释下Lua中的元表和元方法。
假设要对两个表进行加法操作,会先检查是否有元表,且该元表里是否有元方法(_add),如果有的话就可以调用函数计算表的和。
5.说说lua中如何实现面向对象?
封装:可以用两张表来表示一个对象,一个用来保存对象的状态,一个用来保存对象的操作,从而实现私有性。
继承:通过设置元表就可以实现继承,通过设置元表,使用__index元方法让新的对象继承操作,也就相当于实现了共用对象的属性和行为。
多态:重写方法
6.Lua与C交互原理
使用虚拟栈进行交互,当C需要从Lua中获取一个值,只需调用Lua(C通过CAPI调用lua),lua就会将指定的值压入栈。
Lua调用C中函数,需要提前注册。
CAPI包括读写Lua全局变量的函数、调用Lua函数的函数、运行Lua代码段的函数,以及注册C函数(以便后续可被Lua代码调用)的函数。
C#与Lua交互原理:
C#调Lua:C#调用CAPI,通过CAPI调用Lua,然后是通过虚拟栈的形式进行,C#把请求或数据压入栈顶,Lua取出,处理后在压入栈,C#再从栈顶取出结果
Lua调C#:C#生成中间文件,通过CAPI注册到Lua虚拟机,然后lua就可以调用C#了
理解Lua的闭包机制