Lua基础
局部定义与代码块: 使用local声明一个局部变量或局部函数,局部对象只在被声明的那个代码块中有效。 代码块:一个控制结构、一个函数体、一个chunk(一个文件或文本串)(Lua把chunk当做函数处理) 这样,可以在chunk内部声明局部函数,该函数仅在chunk内可见,并且词法定界保证了包内其他函数可以调用此函数。 ? ?在chunk内部定义多个local function并且相互调用(或定义local递归调用函数)时,最好先声明,再定义。 应该尽可能的使用局部变量(使用关键词local声明的变量),有两个好处: 1. 避免命名冲突 2. 访问局部变量的速度比全局变量更快. 可以用do..end来显示的控制local的作用范围,Lua中的do...end就相当于C++中的{},它定义了一个作用域。 ? 多返回值函数: 第一,当作为表达式调用函数时,有以下几种情况: 1. 当调用作为表达式最后一个参数或者仅有一个参数时,根据变量个数函数尽可能多地返回多个值,不足补nil,超出舍去。 2. 其他情况下,函数调用仅返回第一个值(如果没有返回值为nil) 第二,函数调用作为函数参数被调用时,和多值赋值是相同。 第三,函数调用在表构造函数中初始化时,和多值赋值时相同。 另外,return f()这种形式,则返回“f()的返回值”: 可以使用圆括号强制使调用返回一个值。 unpack:函数多值返回的特殊函数,接受一个数组作为输入参数,返回数组的所有元素。 函数可变参数:...表示可变参数,函数体中用arg访问,同时arg还有一个域n表示参数的个数。 多值赋值经常用来交换变量,或将函数调用返回给变量: 调用函数的时候,如果参数列表为空,必须使用()表明是函数调用。 上述规则有一个例外,当函数只有一个参数并且这个参数是字符串或者表构造的时候,()可有可无。 Lua使用的函数,既可是Lua编写的,也可以是其他语言编写的,对于Lua程序员,用什么语言实现的函数使用起来都一样。(优势就在此处) Lua中的函数是带有词法定界(lexical scoping)的第一类值(first-class values)。 第一类值指:在Lua中函数和其他值(数值、字符串)一样,函数可以被存放在变量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值。 词法定界指:嵌套的函数可以访问他外部函数中的变量。这一特性给Lua提供了强大的编程能力。 函数是第一类值(first-class values),也就是说函数名字比如print,实际上是一个指向函数的变量,像持有其他类型值的变量一样。 高级函数(higher-order function):以其他函数作为参数的函数,其实和普通函数没啥区别,只是参数类型是一个函数而已。 将第一类值函数应用在表中是Lua实现面向对象和包机制的关键。 函数的一般化定义应该是这样的: foo = function (x) return 2*x end 而 function?foo (x) return 2*x end 只是函数定义的一个特例。 ? ?Lua中的函数也可以没有名字,也就是匿名函数。 ? 闭包:闭包就是一个函数以及它的upvalues;闭包就是将局部变量和一个函数进行打包维护。 因为Lua函数是带有词法界定的第一类值,闭包才得以实现,而且闭包是一个强大的功能。 upvalues:外部的局部变量(external local variable),也就是被内嵌的函数可以访问的外部那个函数中的变量。这些外部函数的局部变量和内嵌的函数就形成了一个闭包。 迭代器:Lua中迭代器是一个用闭包实现的函数。? 尾调用(Tail Calls):当函数的最后一个动作是调用一个函数时,我们称这种调用为尾调用(明确什么才是尾调用,类似return func(...)这种格式的就是尾调用)。 尾调用不需要再回到调用者函数中,所以不适用额外的栈,这一点很重要,因为正确的尾调用是可以无限制的,并且不会导致栈溢出。 尤其是在写递归逻辑时,不要写成嵌套调用的形式(会不停的压栈),而建议用尾调用的方式来实现,这样效率会高很多。 Lua编译与运行: Lua是解释性语言,但Lua会首先把代码预编译成中间码然后再执行。不要以为需要编译就不是解释型语言,Lua的编译器是语言运行时的一部分,所以,执行编译产生中间码速度会更快。 dofile/dostring和loadfile/loadstring的区别: (1)do*会编译并执行;load*只编译代码生成中间码并且返回编译后的chunk作为一个函数,但不执行代码。 (2)load*较为灵活,发生错误时load*会返回nil和错误信息(可以打印出来)。 (3)如果要运行一个文件多次,load*只需要编译一次,但可以多次运行,do*每次都需要编译。 (4)dostring(str)等价于loadstring(str)() Lua把chunk作为匿名函数处理,例如:chunk "a = 1",loadstring返回与其等价的function () a = 1 end loadfile和loadstring只是编译chunk成为自己内部实现的一个匿名函数,但是这个过程没有定义函数的行为。Lua中的函数定义是发生在运行时的赋值而不是发生在编译时。也就是说loadstring以后,其中的函数还没有被定义,而dostring以后函数就定义好并且可以调用了。 loadstring编译的时候不关心词法范围,也就是说loadstring总是在全局环境中编译他的串,这一点很重要。 local i = 0 f = loadstring("i = i + 1") --使用全局变量i g = function () i = i + 1 end --使用局部变量i 注意:chunks内部可以定义局部变量也可以返回值: 利用asset获取更多的错误信息是个好习惯。 Lua中的错误与异常: Lua中error的处理:Lua经常作为扩展语言嵌入在别的应用中,所以不能当错误发生时简单的崩溃或者退出。相反,当错误发生时Lua结束当前的chunk并返回到应用中。 当Lua遇到不期望的情况时就会抛出错误,你也可以通过调用error函数显式地抛出错误。 当函数遇到异常有两个基本的动作:返回错误代码或者抛出错误。选择哪一种方式,没有固定的规则,不过基本的原则是:对于程序逻辑上能够避免的异常,以抛出错误? error函数:显示的抛出一个错误,终止正在执行的函数,并返回错误信息 assert函数:如果表达式出现错误,则触发一个错误,返回出错信息 assert首先检查第一个参数,若没问题,assert不做任何事情;否则,assert以第二个参数作为错误信息抛出。第二个参数是可选的。注意,assert会首先处理两个参数, 然后才调用函数,所以下面代码,无论n是否为数字,字符串连接操作总会执行: n = io.read() assert(tonumber(n),"invalid input: " .. n .. " is not a number") pcall函数:在保护模式下调用函数(即发生的错误将不会反射给调用者),当调用函数成功能返回true,失败时将返回false加错误信息。pcall不会终止函数的继续运行。我们通过error抛出异常,然后通过pcall捕获之。这种机制提供了强大的能力,足以应付Lua中的各种异常和错误情况。pcall返回错误信息时,已经释放了保存错误发生情况的栈信息。 if?pcall(foo) then --foo是一个函数,pcall(foo)表示调用此函数 ... -- no errors while running `foo' else ... -- `foo' raised an error: take appropriate actions end ? ?如果遇到内部错误(比如对一个非table的值使用索引下标访问)Lua将自己产生错误信息,否则Lua使用传递给error函数的参数作为错误信息。 xpcall函数:xpcall接受两个参数:调用函数、错误处理函数。当错误发生时,Lua会在栈释放以前调用错误处理函数,因此可以使用debug库收集错误相关信息。 debug.debug函数:debug处理函数,可以自己动手察看错误发生时的情况。 debug.trackback函数:debug处理函数,过traceback创建更多的错误信息,也是控制台解释器用来构建错误信息的函数。 ?动态链接库: 通常Lua不包含任何不能用标准C实现的机制,动态链接库是一个特例(动态链接库不是ANSI C的一部分,也就是说在标准C中试线动态链接是很困难的)。 我们可以将动态连接库机制视为其他机制之母:一旦我们拥有了动态连接机制,我们就可以动态的加载Lua中不存在的机制。 loadlib函数提供了Lua中动态链接库的功能。 local path = "/usr/local/lua/lib/libluasocket.so" local f = assert(loadlib(path,"luaopen_socket")) 数据结构: table是Lua中唯一的数据结构,可以通过table来实现其他常用的数据结构。 数组:table下标访问就是数组。习惯上,Lua下标从1开始,Lua的标准库都遵循此惯例,因此自定义的数组下标也最好从1?开始,这样才能使用标准库函数。 矩阵和多维数组:table天生具有稀疏的特性,在表示矩阵时可以节省很多空间,因为那些值为nil的元素不需要存储。 链表:lua中很少会使用这种结构,因为用其他的结构都可以替代。链表的简单实现如下所示: --根节点 list = nil 插入一个值v list = {next = list,value = v} 遍历 local l = list while l do print(l.value) l = l.next end 队列和双向队列:虽然可以使用Lua的table库提供的insert和remove操作来实现队列,但这种方式实现的队列针对大数据量时效率太低,有效的方式是使用两个索引下标,一个表示第一个元素,另一个表示最后一个元素。 List = {} List.new = function () return {first = 0,last = -1} end List.pushleft = function (list,value) local first = list.first - 1 last.first = first list[first] = value end List.pushright = local last = list.last + 1 list.last = last list[last] = value end List.popleft = function (list) local first = list.first if first > list.last then error("list is empty") end local value = list[first] list[first] = nil list.first = first + 1 return value end List.popright = local last = list.last if first > last local value = list[last] list[last] = nil list.last = last - 集合和包:Lua中表示集合有一个简单有效的方法,将所有集合中的元素作为下标存放在一个table里,对于给定的元素,测试该表的对应下标的元素值是否为nil,即可知道该元素是否存在。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |