Lua_Real_Programming
Posted 程序员不是码农
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lua_Real_Programming相关的知识,希望对你有一定的参考价值。
《Programming in Lua》的第二部分,主要介绍Lua中的闭包,模式匹配,日期时间表示,数据结构实现,编译执行和错误方式,以及模块(Modules)和包(Packages)的概念。
Lua的函数function属于first-class的值, 也就是function同Lua中的其他类型number、string、table等所处的地位一样,function也可作为一个变量存储在table中。
那么如何理解function是为first-class呢?以下通过两种函数申明方式来进行解释
这两种声明方式都是申明一个foo的函数,实现2 * x的功能。
第2种申明可如此理解:右侧函数定义作为一条申明语句,创建一个类型为function的值,并把它赋予foo这个变量。甚至,可以理解在Lua中所有的函数都是匿名的,我们平常用到的函数比如print,只不过是把print作为一个变量保存了对应的函数而已。
使用table.sort
来举例说明匿名函数的便利性,根据如下network中name这种字段的字母逆序进行排序
函数声明方式
对于局部函数,需要注意递归的情况,如下申明会出现函数未定义情况。
闭包
先看如下例子:
以上就是一个闭包,newCounter返回一个匿名函数,而这个匿名函数用到了newCounter中定义的local变量count,这个count就是所谓的上值(up-value),每次调用这个返回的匿名函数比如c1()
,这个count貌似已经不在作用域内了,因为newCounter已经返回了,于是,Lua中采用闭包来处理和解决这种情况,每一次的调用,这个count的值会加一并保存。当创建一个新的匿名函数比如c2,c2的count跟c1拥有的count是完全分开独立,互不干扰的,也就是说c2产生了一个新的闭包。因此c1和c2是基于同一个匿名函数的不同闭包。
闭包让Lua函数的功能变得更强大,以下是使用闭包的例子:
Lua模式匹配的实现代码不到600行,没有实现所有POSIX正则表达式的全部功能,但是Lua模式匹配功能强大,提供了很多feature是在标准的POSIX中很难做到的。
模式匹配相关函数
string.gsub, 这个接口之前已有介绍,不再赘述。
string.find
string.find (s, pattern [, init [, plain]])
寻找字符串s中pattern匹配的位置,匹配成功则返回两个索引值(pattern的起始和终止位置),否则返回nil。
第三个可选参数init表明从哪里开始搜索,其默认值是1
第四个可选参数plain表明是否为普通的字符串子串的搜索,不考虑模式匹配,如下:
string.match
string.match (s, pattern [, init])
跟string.find
类似,但是其不是返回成功匹配的位置,而是返回成功匹配的内容,第三个默认参数init同样为指定搜索起点。例子如下:
string.gmatch
string.gmatch(s, pattern)
返回一个迭代器函数,遍历字符串中所有出现的pattern。
Patterns模式
Lua中使用%
来作为转义字符,常用字符含义如下
所有转义字符的大写,表示其补集,例如%A
代表所有的非字母字符(%a为所有字符),如:
在Lua的模式中有一些字符具有特殊含义,被称为Magic Characters, 如下:
Lua提供了4种修改器如下:
Exercise
请编写一个函数split,该函数接受两个参数,第一个参数是字符串,第二个参数是分隔符模式:
Lua中日期和时间有两种表示方式:一种是整数形式,一种是table形式(日期table有以下这些域:year, month, day, hour, min, sec, way, yay, and isdst)。
os.time
os.date
其功能跟os.time
相反,是将数字表示的时间和日期转换成date table或者字符串等更加易读的形式。
例如:
Date-Time操作
计算从此刻起40天以后那天的日期
Lua可以用table高效的实现数组,列表,队列,集合等数据结构。
Arrays数组
Linked List列表
Queue队列
Sets集合
将元素作为key放入table中,value置为true即可
String buffer字符串缓存
假定我们需要从文件中一行行读取数据,可以选用以下方法:
这种方式效率极低,前面我们也提高过..
进行字符串拼接是很低效的(一次拼接就是一个内存分配和字符移动)。因此可以选用更为高效的方式table.concat
Compilation
loadfile
从文件中加载Lua代码,只编译代码并将编译结果作为函数返回,dofile
是基于loadfile
,会执行函数,可定义如下:
dofile
更加完整,但是loadfile
更加灵活,可只编译一次,运行多次,而dofile
每次运行都需要编译,开销大。
load
跟loadfile
功能类似,但是load
不是从文件中读取Lua代码,而是从字符串或者函数中读取,如:
在加载定义了函数的代码块时,有一个坑需要注意,lua中定义是需要通过赋值的:
当使用loadfile加载上述两个文件时,加载foo1.lua后foo函数是没有被定义,需要执行之后才可使用,而foo2.lua被加载后foo可直接使用,如下:
Errors
处理错误的方式:
可以使用pcall
在lua代码内部处理错误,不过使用pcall是看不到traceback的,因为其在返回错误代码时就已经销毁了调用栈。
模块:是能够通过函数require
加载,然后返回一个table,任何模块导出的函数和常量,都定义在table中,这个table可以看作是一个命名空间。
Packages:由许多模块组成。
模块在lua中不是first-class,其不能作为参数传递给函数。
require加载模块时,会从package.path路径中去搜索,如果找到了对应文件,就通过loadfile进行加载。如果package.path中不能找对应的lua文件,就会从c库中去搜索,即package.cpath,如果在c库中找到了,就通过package.loadlib去找luaopen_modname的函数,其中modname为模块名称,luaopen_modname表示一个lua函数。
路径搜索
require
搜索路径由一系列模版组成,模版之间用;
隔开,如下;
如果调用require sql
,直接用sql替换上述路径中的?
,那么其搜索顺序如下:
以下为实现一个类似package.searchpath
的例子
首先要把modname中的.
换成/
,然后将modname替换path中的?
。
Lua中实现模块的方法
定义一个table,将所有函数放入table中,并返回table即可:
主要介绍Lua中闭包概念以及其用途,Lua的模式匹配,日期时间的两种表现形式以及相关转换接口,如何使用table实现其他数据结构如数组,列表,队列,集合等,实现,编译执行和错误方式,最后介绍了模块(Modules)和包(Packages)的概念。
参考:
http://www.lua.org/pil/
http://www.lua.org/manual/5.3/manual.html#lua_call
以上是关于Lua_Real_Programming的主要内容,如果未能解决你的问题,请参考以下文章