江枫

Lua基础知识

1.Lua的8种基本数据类型:

   nil,string,number,boolean,table,function,thread,userdata
   nil表示空值,和其他类型都不相等,在条件判断中只有nil和false表示否定。 number表示值类型,5.3以前lua是不区分整型和浮点型,5.3以后新增了整型作为number的子类型,可以使用math.type判断一个值是整型还是浮点型。

2.table是唯一的数据结构

   table是lua唯一的数据结构,其他结构都可以用table实现,它不仅可以用数值作索引,也可以用其他类型(比如字符串,函数,甚至另一个table),lua内部用一个数组和一个哈希表来实现table。这使得table非常强大,即可以用作字典,也可以用作数组,配合元表机制还可以模拟面向对象。lua的很多基础设施,比如模块,全局变量,元表,都是基于table实现的。在语言层面它是如此简洁,可又能实现无比强大的功能。 在table中要谨慎的使用nil,例:

   两种表达方式,得到的结果不一样,所以在table中不能使用nil,应该使用false代替。

3.函数

   Lua函数支持多返回值,也支持可变参数,例:

   pcall与xpcall: lua可以使用pcall, xpcall来实现错误的捕获,在pcall中执行的函数如果出错,会返回false,后面带一个错误消息:(pcall直接调用该函数,后面跟函数的参数)

   pcall返回的第1个值表明test函数是否成功执行,如果为true,则后面的返回值就是test函数的返回值;如果为false,后面的值是一个错误消息。但是pcall有时候并不能满足要求,比如我们想知道错误是在哪里发生的,那么就要用xpcall来实现:(xpcall第一个参数为要调用的函数,第二个参数为调用失败时的回调函数,第三个参数开始为要调用函数的参数列表)

   内部函数里可以引用外部函数的局部变量,这些引用被称为upvalue,而Lua的函数也可以叫闭包。闭包和upvalue是非常重要的特性。 作为一个典型的应用,假设要创建一个对象,对外只提供有限的访问接口,而对象内部的数据不能直接被修改,那么我们可以这样写:

4.元表

   给一个table设置元表,当对这个table执行特定操作时,如果元表存在对应的元方法,则会调用该元方法。元表大概是这样设置的:

   元方法:
   1.__tostring
   如果t的元表存在tostring元方法,调用tostring(t)时(或者print(t)时,因为print内部会自动调用tostring),会调用元表的tostring方法,这样我们就可以对t进行序列化,下面是一个例子:

   2.__pairs
   当调用pairs(t)时,如果元表存在__pairs,就会调用该元方法并传入t。这可以实现对表的特殊遍历。
   我们想遍历出值是number类型的元素,这时就可以使用__pairs来实现,在实现之前我们先对泛型for循环作一个介绍,pairs返回3个变量:迭代器函数(比如next),表,当前索引。for循环从pairs得到这3个变量后,会调用迭代函数,如:next(t, index),一开始index为nil,这样next就返回第一个index和value。 接着for循环不断的调用next并传入上一次调用得到的index,直到next返回nil为止。

   3.__len
   如果t是一个数组,用#t可以取得该数组的长度, 但如果是字典就不行了,必须用__len元方法:

   4.__index__newindex
   这两个元方法用于对table的读写控制。
   读表中key关联的值,如果key不存在,会调用元表的index。index可以是一个函数__index(t, k);也可以是另一个表,此时会直接读取该表的key值。如果不想调用t[k]时触发元方法,可以使用rawget(t, k)。
   要设置表中key关联的值,如果key不存在,会调用元表的newindex。同样newindex可以是一个函数__newindex(t, k, v);也可以是另一张表,此时会直接向该表的key设置值。如果不想t[k]=v时触发元方法,可以使用rawset(t, k, v)。
   这两个元方法,可用于实现类似面向对象的机制。
   P.S. rawget与rawset就是不使用元表和元方法的情况下对表的访问和赋值。
   5.__call
   这个元方法可使非函数对象像函数一样调用,比如一个表t,我们这样写:t("hi"),如果它的元表存在__call函数,则会触发调用:

5.Debug库

   debug.debug 进入命令行交互模式,在这里相当于进入了虚拟机内部,可以查看全局变量,但是这是一个独立的上下文环境,不能查看调用它的函数的局部变量。
   debug.getinfo ([thread,] f [, what]) 取函数调试信息,f可以是一个函数,也可以是调用堆栈的层数,1表示调用getinfo的函数,2表示再上一层,以此类推。what指定想查哪些信息,不指定表示全部信息。
   debug.getlocal ([thread,] f, local) 取函数的本地变量,注意f是栈的层次,和上面一样1表示调用debug.getlocal的函数。local从1开始取函数参数和本地变量,从-1开始是可变参数,返回名字和值。如果取不到返回nil
   debug.getupvalue (f, up) 取函数引用的upvalue,f是一个函数对象,up从1开始,返回名字和值。如果取不到返回nil。
   debug.sethook ([thread,] hook, mask [, count]),设置一个hook,当指定事件发生时,这个hook会被回调,hook是回调函数,mask表明想触发什么事件,如果mask为空,count须指定一个值表示执行多少条指令后触发。mask是下面值的组合:

  • 'c' 每次调用一个函数触发。
  • 'l' 每执行一行触发。
  • 'r' 每次从一个函数返回触发

   debug.traceback ([thread,] [message [, level]]) 返用调用堆栈的信息,message表示返回的头部加一段信息,level表示栈的层次,1表示调用debug.traceback那个函数,往上类推。
   debug.setupvalue (f, up, value) 给函数f设置upvalue,up同样是一个序号
   debug.setlocal ([thread,] level, local, value) 给函数设置本地变量, level是栈层次,local是本地变量的序号,value是值。

6.协程

   输出结果:

文章大纲