还是在工作之后才听说的Lua脚本语言--游戏制作领域的脚本语言。不过算来也不是第一次接触,原先喜欢的游戏魔兽世界就是以lua作为脚本语言的。 lua在的读法是"鲁啊"是月亮的意思。
·要点: 基础语法: --常规说明: 注释方法:单行注释方法是以两个连字符(--)开始一个行注释; 块注释方法是以"—[["开始以"]]"(推荐是"—]]")结尾, 可在[[之间加任意=,而对应的]]之间也要有相同数量的=; 程序块:使用关键字do (换行) <body> (换行) end的形式; 与Python一样不需要使用";"表示语句的结束; 特殊类型nil表示无效值--为初始化的全局变量就是nil,也可通过赋值nil来删除 变量--这是垃圾回收器会自动销毁变量;
类型和值: lua中的类型有:nil,boolean,number,string,userdata,function,thread, table(核心,同时也是唯一的数据结构); nil:用于表示"无效值"的概念; boolean:可选值true/false,其中为假值的情况只有false和nil; number:表示实数,lua中没有整数类型; userdata:主要用于跟C程序交换数据时使用; function:函数--作为"第一类值"来看待; thread:协同程序中使用; string:不可变值,表示"一个字符序列"; 完全采用8位编码--可将任意二进制数据存储到一个字符串中; 可采用单引号和双引号两种方式定义,支持转移字符; 多行字符串定义可采用[=[和]=]形式,其中=数量任意; 字符串连接操作符是".."--如果后接数字需要添加空格; 系统提供数字和字符串的自动转换; 操作符"#"可以获取字符串的长度; table:有点像数据表,索引可以是除nil外的任意值; 可以动态增减大小,是lua中主要且仅有的数据结构机制; 通过table可以实现数组、符号表、集合、记录、队列等数据结构; 模块、包和全局空间都是table结构,同时面向对象也通过table实现; 在Lua中table是"对象"--即使用的是table的应用; 使用构造表达式"{}"来创建table; table永远是"匿名的",变量只是持有的对table的应用; 访问table元素有两种方法:a["key"]和a.key是等价的; 需要注意的是table的索引默认的话是从1开始的--这和其他语言不同; 通过操作符#可获取table的最大数字索引值--需小心索引空隙;
表达式: --算术操作符:有-(负号),+,-,*,/,^(指数),% 其中取模%规则是:a%b=a-floor(a/b)*b; 对于整数是通常意义上的取模操作,结果符号与b相同; 对于实数,x%1得到x的小数部分; 对于实数x,x-x%0.01得到x精度到小数点后两位的结果; --关系操作符:有<,<=,>,>=,==和~=六个; nil自与自身相等; 对table,userdata和fucntion比较引用--引用相同对象才相等; --逻辑操作符:有and,or和not三个; and和or都使用短路求值; 惯用法:x=x or v用于对未初始化x设置默认值v; 惯用法:(a and b) or c类似于C++中的a?b:c语法; --字符串连接操作符:操作符".."(两个点) 即使两个操作符都是数字也会先将数字转变为字符串在连接; 字符串是不可变量,所以连接操作后得到的是新字符串; --操作符优先级:^>(not#-)>(*/%)>(+-)>..>(<><=>=~===)>and>or --table构造式:列表风格:a={"Sunday","Monday","Tuesday"} 记录风格:a={x=10,y=-2}
流程控制语句: --赋值:修改变量或table元素值,更改变量的引用; Lua支持多重赋值--可交换变量和函数返回多个值; --局部变量与块:通过local语句可以创建局部变量; --分支控制if:if语句格式:if cond then body elseif cond then body else body end --whil循环:while格式:while cond do body end --repeat循环:repeat格式:repeat body until cond repeat应该是替换C++中的do…while循环; --数字型for循环:for var=exp1,exp2,exp3 do body end --泛型for循环:通过迭代器函数来遍历所有值; for k[,v] in pairs(t) do body end --break和return: break语句用于结束一个循环; return语句主要用于在函数中返回结果和结束函数执行; 需要注意的一点是break和return只能是一个块的最后一条语句;
函数:函数是一种对语句和表达式进行抽象的主要机制; 在lua中如果参数是字面字符串或table构造式函数调用可以省略括号; 对象调用方法有两种方式:obj.func(x,y)和obj:func(x,y); 函数定义方法:function func_name(args) body end lua会自动匹配参数和实参--多余舍去不足用nil初始化; 默认实参:使用参数自动匹配和n=n or 1语句来设置默认参数; --多返回值:lua会调整返回值的数量以适应不同的调用情况: 只有作为表达式最后一个元素时才获取所有参数,否则只得到第一个返回值; 用到多返回值的情况:多重赋值、函数实参、table构造式、return语句; 在return语句中,使用括号将强制值返回第一个返回值; --变长参数:语法格式:fucntion fucn_name(arg1,…) body end 操作符"..."表示该函数可以接受不同的实参--即变长参数; 表达式"..."表示一个由所有变长参数构成的数组; 使用:"a,b=…"的形式或"for i,v in ipairs{…}"或"return …"形式; 可通过select()函数来访问可变参数"..."中指定位置的参数; --具名参数:具名参数是指调用是采用"func_name(a=b)"的形式来指定实参; 可通过将参数设定为table的方法来间接实现具名参数; --匿名函数:定义形式:foo=function (x) return 2*x end的形式; --闭合函数:Lua中可在函数中定义函数,且内部函数可访问外部函数的局部变量; 外部函数的局部变量在内部函数中叫非局部变量--不会在外部函数推出后失效; closure(闭合函数)是{函数+该函数所需访问的所有非局部变量}; 同一函数的不同closure的非局部变量是不相干的; 利用闭合函数(closure)可以进行函数式编程; --非全局的函数:指将函数存储在table字段或局部变量中的函数; 局部函数定义:local f=function (args) body end local function foo(args) body end table字段函数:tab.foo=function (args) body end tab={foo=function (args) body end} function tab.foo(args) body end --递归函数:在定义递归函数时需要先将函数进行local func初始化; --尾调用:只有"return func_name(args)"形式作为结尾才是尾调用; 尾调用市不会保留上一个函数状态--类似goto不会保留函数调用链; 一个典型应用是编写"状态机";
迭代器与泛型for: 迭代器是一种可以遍历集合中所有元素的机制--主要为for编写; 迭代器是调用一次返回集合中下一个元素的函数; 泛型for会做所有的薄记工作,而迭代器只需要正确返回集合中下一个元素; 迭代器包含两部分:工厂函数如ipairs()函数和工作器函数; 为了正确返回集合中的下一个元素,迭代器需要保存状态: 使用closure机制; 使用泛型for的恒定状态和控制变量来记录; 扩展恒定状态为table来传递复杂状态;
编译、执行和错误: Lua允许先将源代码预编译为一种中间形式,相关函数: dofile():编译并执行lua文件; loadfile():编译lua文件但不执行,以函数形式返回而函数调用就是执行; loadstring():与loadfile()功能一样,但参数是string; 底层函数package.loadlib()可以加载动态库; --错误处理: 作为脚本语言,lua把异常处理交给了宿主语言来处理而只提供错误信息; 错误处理方式: 返回nil和错误信息; 通过error()函数抛出异常,相关build-in函数是assert()函数; 可以用pcall()函数包装需要执行的代码;
协同程序(coroutine): 协同程序是一条执行序列,拥有自己独立的栈、局部变量和指令指针, 同时与其他协同程序共享全局变量和其他大部分共享资源; 与线程的区别: 协同程序像同步后的线程,每次只有一个协同程序在执行; 协同程序只能自己要求挂起(可通过其他方式改变); 所有的协同程序相关函数放在"coroutine"的table(库)中; 协同程序的数据类型为thread类型; 协同程序的4种状态: 挂起(suspended):创建和调用yield()后进入该状态; 运行(running):通过resume()启动协同程序后进入该状态; 死亡(dead):执行完协同程序后进入此状态; 正常(normal):调用resume的协同程序处于此状态; 相关函数: coroutine.create(func):创建协同程序; coroutine.resume():启动协同程序; coroutine.yield():挂起协同程序; coroutine.statue():检查协同程序的状态; 一个比较好的例子是生产者/消费者模型;
数据结构: Lua中的数据结构都是通过table来实现的; --数组:使用数字做索引的table; 需要注意的是Lua中的惯例是索引从1开始的; --矩阵和多维数组: 实现多维数组的两种方法:一种是使用table中嵌套table的方法; 还一种是将二位数组变更为一维后存储在table中; 因为table本身就是稀疏的,所以用table实现的稀疏表不存在内存开销问题; --链表:只需要将table的一个字段持有下一个table的引用就可以了; --队列和双向队列:通过table库的插入/删除函数可模拟队列行为; --集合与无序组:可将table索引定义为元素,将值定义为真假值来实现集合概念; --字符串缓冲:因为字符串是不可变值,像连接操作会产生新字符串开销, 可使用table来缓存字符,最后用table.concat()来连接成字符串; --图:也可通过table来表示图的概念; 对应不同的图表示有不同的算法匹配;
数据文件与持久性: --数据文件: 数据文件指按固定格式(如html/xml等)存储数据的文件; lua拥有自己的数据存储方法:Enty{}或Enty{key=value}形式, 可在程序中定义Enty函数来处理数据 --因为调用table可省略括号所以数据就变成了函数调用; --持久性:可将lua代码保存为字符串形式--需要注意转移字符;
元表与元方法: 元表:定义了一个table行为属性的另一个table; 元方法:元表中定义的行为函数; table和userdata像类一样可以定义自己的独立元表, 而其他类型则共享所属类型的元表行为,且需要通过C代码设置; 相关函数:getmetatable()和setmetatable()两个函数; --算术类的元方法:__add,__sub,__mul,__div,__unm(取反),__mod, __pow,__cancat; 查找元方法顺序:先在第一个值元表中查找,然后是第二个值,都没有会报错; --关系类元方法:__eq(==),__lt(<),__le(<=)三种基础比较; 其他的关系比较操作都是通过上述三种基础比较操作来实现; 不支持混合比较和拥有不同元方法的比较; --库定义的元方法:程序库在元表中定义自己的字段; 常用字段:metatable.__tostring():printh函数调用时会调用; metatable.__metatable():保护metatable不会被得到/修改; --table访问的元方法:提供改变table行为的方法,相关方法: __index:查找元素是调用,在访问table中字段时,先在tablez中查找, 然后调用__index()函数返回结果,都不存在时返回nil; __index既可以赋值为函数也可以赋值为table; 可通过函数rawget来禁用__index的方法; __newindex元方法:用于赋值给不存在的字段时调用; 当__newindex赋值为table的话,调用__newindex会 对绑定的table赋值而不是更改原table; 使用__index和__newindex可实现:只读table/具有默认值table/继承关系;
环境:具有一定生命期的保存相应数据的table结构; lua将所全局变量保存在环境table中--环境table自身保存在全局变量_G中; 为防止对全局变量的误操作可以给全局环境table设置元表; 可通过函数setfenv()来设置函数的环境,并可以通过在非全局环境中包含 全局环境来使用全局变量;
模块与包: 模块就是程序库,而包则是一个完整的模块库--整合库/Lua的发行单位; 模块和包的搜索路径:当前目录=》环境配置目录,会查找lua文件和C文件; 包采用文件夹结构,需要包含一个init.lua的文件; --加载: 加载函数是require()--当参数是字符串时一般省略括号; 函数require()会加载模块中的变量和函数等数据结构到全局变量中; --编写module: 在文件开始创建table,中间定义字段函数,最后返回table; 为了增加模块特性可扩展模块结构; 新增函数module会自动应用扩展来增强模块特性;
面向对象编程: 在Lua中更能体现面向对象是一种思想--通过table实现面向对象编程; 用惯了C++方式的面向对象方法在使用Lua的面向对象很不习惯; --类: 通过table的元表和__index元方法可实现类的概念; 通过在元表中定义new方法可实现类的构造函数; 语法obj.func(obj,args)等价于obj:func(args)--可隐藏self/this; --继承: 单继承的实现通过元表和__index表来实现; --多重继承: 通过__index函数来实现多继承--但以为函数调用存在性能开销; --私密性即封装: 将方法和数据分离放入不同的table中--不常用的技巧;
弱引用table: 弱引用—一种会被垃圾收集器忽视的对象引用
弱引用table:具有弱引用条目的table
Lua只会回收弱引用table中的对象—而不是值(字符串也是值)
实现方法是通过元表的__mode字段设置为带k/v的字符串
--备忘录函数
根据空间换时间的思想可缓存计算结果来减少频繁操作的耗时
而缓存的存储则花费空间—即内存
弱引用可以在结果没有使用时由垃圾回收器自动回收内存
--对象属性
将对象作为key来关联属性时,如引用解决了无法删除对象key
弱引用table的应用--回顾table的默认值
标准库:
--I/O库:
--数学库:由一组标准的函数构成:
三角函数(sin,cos,tan,asin,acos等),指数对数函数(exp,log,log10等),
取整函数(floor,ceil,max和min),变量pi和huge(最大数字),
生成伪随机数函数(math.random,math.randomseed)
--table库:由一些辅助函数构成,这些函数将table作为数组来操作:
插入和删除(table.insert,table.remove),排序(table.sort),
连接(table.concat)
--字符串库:
原始字符串操作:创建字符串,连接字符串和获取字符串长度
在5.1中也将string库的函数导出为字符串类型的方法(元表实现)
表示位置时需要注意的是:起始位置为1且负数表示从结尾计数
因为字符串是不可变值,所以返回字符串都是新字符串
基础字符串函数:string.len(s),string.rep(s,n),string.lower(s),
string.upper(s),string.sub,string.charstring.byte,
t={s:byte(1,-1)},string.char(unpack(t)),string.format;
--模式匹配函数
没有采用POSIX和perl的正则表达式方式
相关函数:
string.find –找到完全匹配时返回起始索引和结尾索引
--也可以含开始搜索的开始位置参数
string.match –与string.find类似,但返回的是子串
string.gsub –用指定参数替换所有匹配
--也有可选参数来指定替换次数
string.gmatch –返回的是函数,用在泛型for中可作为迭代器使用
模式
跟正则表达式中的模式是一种概念,但是lua自己的规则
定义好的字符分类
.(所有字符)%a(字母)%d(数字)%w(字母和数字字符)
%l(小写字母)%u(大写字母)
%c(控制字符)%p(标点符号)%s(空白字符)
%x(十六进制数字)%z(内部表示为0的字符)
--大写形式表示的是补集
魔法字符—需要用%来转义
--().%+*-?[]^$
--因为这些字符都有特殊含义需要转义符号%来转义
自定义字符分类
--定义方式是用[]将规则放入其中间位置,如二进制[01]
--可以通过符号”-”表示区间,如八进制[1-7]
--符号”^”表示取反操作—即得到补集
重复性修饰符
+ 重复1次或多次
* 重复0次或多次
- 也是重复0次或多次
? 出现0或1次,表示可选概念
--其中-和*的区别可以通过例子来理解:
对于C++中/*和*/,符号*会尽可能多的匹配—即匹配最后*/
而符号-则尽可能少的扩展来找到第一个*/
特殊符号
如果模式以^开头则只会匹配目标字符串的开头部分
如果模式以$结尾则只会匹配目标字符串的结尾部分
可使用%b<x><y>来匹配成对的字符
捕获
捕获的概念是有选择的从目标字符串中提取匹配内容
表示为将需要捕获的模式放入在()中
可以使用”%数字”的形式来引用其他捕获
替换
主要是扩展string.gsub函数,即替换的目标可以是函数或table
当第三个函数或table字段返回nil时不做替换
技巧
--没看
--操作系统库:
--调试库:
·小结:
|
请发表评论