lua学习笔记

from

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。

特性

  • 轻量级、可扩展
  • 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
  • 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
  • 语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
  • 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。

应用场景

  • 游戏开发
  • 独立应用脚本
  • Web 应用脚本
  • 扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
  • 安全系统,如入侵检测系统

环境安装

在mac下直接brew install lua就能装上。

基本语法

提供交互式编程与脚本式编程。交互式编程通过命令lua or lua -i启动。脚本以lua结尾的文件,指定Lua 的解释器 #!/usr/local/bin directory,为脚本添加可执行权。

注释

单行注释为

--

多行注释

--[[
  多行注释
  多行溈
--]]

标示符

与C语言类似。最好不要使用下划线加大写字母的标示符,以防止与Lua的保留字冲突。

数据类型

动态类型。

基本类型分别为: nil、boolean、number、string、userdata、function、thread和table。

比较特别的:

  • nil 还有一个"删除"作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉
  • userdata 表示任意存储在变量中的C数据结构
  • table为一个关联数组,数组的索引可以是数组or字符串
  • 字符串由一对双引号或单引号来表示,也可以用 2 个方括号 "[[]]" 来表示"一块"字符串。

可以使用type函数测试给定变量的类型:

print(type("Hello world"))      --> string

变量

三种类型:全局变量、局部变量、表中的域

全是全局变量,除非用local显示声明为局部变量。

局部变量的作用域从声明位置开始到所在语句块结束。

应该尽可能地使用局部变量,两个好处:

  1. 避免命名冲突
  2. 访问局部变量的速度比全局变量更快。

循环

while循环和c中比较相像

for 循环

包括两类:

  1. 数值for循环
  2. 泛型for循环

数值for循环

for var=exp1, exp2, exp3 do 
    <执行体>
end

var从exp1变化到exp2,每次变化以exp3为步长递增var,并执行一次"执行体"。exp3是可选的,如果不指定,默认为1。

泛型for循环

通过一个迭代器函数来遍历所有值。

例:

for i,v in ipairs(a)
        do print(v)
end

ipairs是Lua提供的一个迭代器函数。

流程控制

Lua认为false和nil为假,true和非nil为真。要注意的是Lua中 0 为 true。

if语法:

if(布尔表达式)
then
   --[ 在布尔表达式为 true 时执行的语句 --]
end

if-else语法:

if(布尔表达式)
then
   --[ 布尔表达式为 true 时执行该语句块 --]
else
   --[ 布尔表达式为 false 时执行该语句块 --]
end

嵌套语法:

if( 布尔表达式 1)
then
   --[ 布尔表达式 1 为 true 时执行该语句块 --]
   if(布尔表达式 2)
   then
      --[ 布尔表达式 2 为 true 时执行该语句块 --]
   end
end

函数

定义格式:

optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
    function_body
    return result_params_comma_separated
end
  • optional_function_scope: 可选,未设置则为全局,可设为local
  • result_params_comma_separated: 可返回多个值,以逗号隔开

可变参数

和C语言类似在函数参数列表中使用三点(...) 表示函数有可变的参数。

Lua将函数的参数放在一个叫arg的表中,#arg 表示传入参数的个数。

例:

function average(...)
   result = 0
   local arg={...}
   for i,v in ipairs(arg) do
      result = result + v
   end
   print("总共传入 " .. #arg .. " 个数")
   return result/#arg
end

print("平均值为",average(10,5,3,4,5,6))

运算符

算术

加减乘除、取余、乘幂、负号

关系

  • 等于 ==
  • 不等于 ~=
  • > 大于
  • < 小于
  • >= 大于
  • <= 小于

逻辑

and or not

其他

  • .. 用来连接两个字符串
  • 返回字符串或表的长度

字符串

可以使用以下三种方式来表示:

  • 单引号间的一串字符。
  • 双引号间的一串字符。
  • [[和]]间的一串字符。

字符串操作

  1. string.upper(argument):字符串全部转为大写字母。
  2. string.lower(argument):字符串全部转为小写字母。
  3. string.gsub(mainString,findString,replaceString,num)在字符串中替换,mainString为要替换的字符串, findString为被替换的字符,replaceString要替换的字符,num 替换次数(可以忽略,则全部替换)
  4. string.strfind (str, substr, [init, [end]])在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置。不存在则返回 nil。
  5. string.reverse(arg):字符串反转
  6. string.format(...):返回一个类似printf的格式化字符串
  7. string.char(arg) 和 string.byte(arg[,int])char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。
  8. string.len(arg):计算字符串长度
  9. string.rep(string, n)):返回字符串string的n个拷贝
  10. .. 链接两个字符串

数组

一维数组

我们可以使用整数索引来访问数组元素,如果知道的索引没有值则返回nil。

在 Lua 索引值是以 1 为起始,但你也可以指定 0 开始。

除此外我们还可以以负数为数组索引值。

多维数组

多维数组即数组中包含数组或一维数组的索引键对应一个数组。

例:

-- 初始化数组
array = {}
for i=1,3 do
   array[i] = {}
      for j=1,3 do
         array[i][j] = i*j
      end
end

-- 访问数组
for i=1,3 do
   for j=1,3 do
      print(array[i][j])
   end
end

迭代器

泛型for迭代器

泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。

泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:

for k, v in pairs(t) do
    print(k, v)
end

无状态的迭代器

无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。

每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。

这种无状态迭代器的典型的简单的例子是ipairs,他遍历数组的每一个元素。

多状态的迭代器

很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到table内,将table作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在table内,所以迭代函数通常不需要第二个参数。

Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。

Lua table 是不固定大小的,你可以根据自己需要进行扩容。

当我们为 table a 并设置元素,然后将 a 赋值给 b,则 a 与 b 都指向同一个内存。如果 a 设置为 nil ,则 b 同样能访问 table 的元素。如果没有指定的变量指向a,Lua的垃圾回收机制会清理相对应的内存。

Table 操作

  1. table.concat (table [, sep [, start [, end]]]):concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。
  2. table.insert (table, [pos,] value):在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾
  3. table.maxn (table)指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0。
  4. table.remove (table [, pos])返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。
  5. table.sort (table [, comp])对给定的table进行升序排序。

模块与包

把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。Lua的模块是由变量、函数等已知元素组成的table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。

使用require的函数加载模块。

require "<模块名>" or ("<模块名>")

加载机制

require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。

当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),例如把 "~/lua/" 路径加入 LUA_PATH 环境变量里:

#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"

source ~/.profile

C包

与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。

元表

两个函数处理元表:

  • setmetatable(table,metatable): 对指定table设置元表(metatable),如果元表(metatable)中存在__metatable键值,setmetatable会失败 。
  • getmetatable(table): 返回对象的元表(metatable)。

__index元方法

  • 当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。

    如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。

__newindex元方法

  • __newindex 元方法__newindex 元方法用来对表更新,__index则用来对表访问。当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。

为表添加操作符

模式描述
__add对应的运算符 '+'.
__sub对应的运算符 '-'.
__mul对应的运算符 '*'.
__div对应的运算符 '/'.
__mod对应的运算符 '%'.
__unm对应的运算符 '-'.
__concat对应的运算符 '..'.
__eq对应的运算符 '=='.
__lt对应的运算符 '<'.
__le对应的运算符 '<='.

__call 元方法

__call 元方法在 Lua 调用一个值时调用。

__tostring 元方法

__tostring 元方法用于修改表的输出行为

协同程序 (coroutine)

Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。

线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。

方法描述
coroutine.create()创建coroutine,返回coroutine, 参数是一个函数,当和resume配合使用的时候就唤醒函数调用
coroutine.resume()重启coroutine,和create配合使用
coroutine.yield()挂起coroutine,将coroutine设置为挂起状态,这个和resume配合使用能有很多有用的效果
coroutine.status()查看coroutine的状态。注:coroutine的状态有三种:dead,suspend,running
coroutine.wrap()创建coroutine,返回一个函数,一旦你调用这个函数,就进入coroutine,和create功能重复
coroutine.running()返回正在跑的coroutine,一个coroutine就是一个线程,当使用running的时候,就是返回一个corouting的线程号

resume和yield的配合强大之处在于,resume处于主程中,它将外部状态(数据)传入到协同程序内部;而yield则将内部的状态(数据)返回到主程中。

文件IO

  • 简单模式(simple model)拥有一个当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作。
  • 完全模式(complete model使用外部的文件句柄来实现。它以一种面对对象的形式,将所有的文件操作定义为文件句柄的方法

IO方法

io.open/close/input/output/read/write/tmpfile/type/flush/lines

完全模式

用来同一时间处理多个文件,使用 file:function_name 来代替 io.function_name 方法。

错误处理

分为:语法错误 和 运行错误

可以使用 assert和error来处理。

assert

例:

local function add(a,b)
   assert(type(a) == "number", "a 不是一个数字")
   assert(type(b) == "number", "b 不是一个数字")
   return a+b
end
add(10)

error

语法格式:

error (message [, level])

功能:终止正在执行的函数,并返回message的内容作为错误信息(error函数永远都不会返回)

通常情况下,error会附加一些错误位置的信息到message头部。

Level参数指示获得错误的位置:

  • Level=1[默认]:为调用error位置(文件+行号)
  • Level=2:指出哪个调用error的函数的函数
  • Level=0:不添加错误位置信息

pcall 和 xpcall、 debug

Lua中处理错误,可以使用函数pcall(protected call)来包装需要执行的代码。

Lua提供了xpcall函数,xpcall接收第二个参数——一个错误处理函数,当错误发生时,Lua会在调用桟展看(unwind)前调用错误处理函数,于是就可以在这个函数中使用debug库来获取关于错误的额外信息了。

debug库提供了两个通用的错误处理函数:

  • debug.debug:提供一个Lua提示符,让用户来价差错误的原因
  • debug.traceback:根据调用桟来构建一个扩展的错误消息

DEBUG

函数比较多,需要用的时候再去查阅。

  • 命令行调试器有:RemDebug、clidebugger、ctrace、xdbLua、LuaInterface - Debugger、Rldb、ModDebug。
  • 图形界调试器有:SciTE、Decoda、ZeroBrane Studio、akdebugger、luaedit。

垃圾回收

采用了自动内存管理。Lua 运行了一个垃圾收集器来收集所有死对象(即在Lua中不可能再访问到的对象)来完成自动内存管理的工作。

Lua 实现了一个增量标记-扫描收集器。 它使用这两个数字来控制垃圾收集循环: 垃圾收集器间歇率和垃圾收集器步进倍率。

  • 垃圾收集器间歇率控制着收集器需要在开启新的循环前要等待多久。增大这个值会减少收集器的积极性。
  • 垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。增大这个值不仅会让收集器更加积极,还会增加每个增量步骤的长度。

垃圾回收器函数

Lua 提供了以下函数collectgarbage ([opt [, arg]])用来控制自动内存管理:

  • collectgarbage("collect"): 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:
  • collectgarbage("count"): 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。
  • collectgarbage("restart"): 重启垃圾收集器的自动运行。
  • collectgarbage("setpause"): 将 arg 设为收集器的 间歇率 (参见 §2.5)。 返回 间歇率 的前一个值。
  • collectgarbage("setstepmul"): 返回 步进倍率 的前一个值。
  • collectgarbage("step"): 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。
  • collectgarbage("stop"): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。

面向对象

//TODO

数据库访问

LuaSQL库:url

Lua 5.3 参考手册

手册url