Lua 中文速查手册
Lua 特性
- 轻量级:源码2.5万行左右C代码, 方便嵌入进宿主语言(C/C++)
- 可扩展:提供了易于使用的扩展接口和机制, 使用宿主语言提供的功能
- 高效性:运行最快的脚本语言之一
- 可移植:跨平台
入门书籍《lua 程序设计》
推荐:云风翻译的《Lua 5.3参考手册》http://cloudwu.github.io/lua53doc/manual.html
变量
作为动态类型语言,变量本身没有类型, 赋值决定某一时刻变量的类型。私有静态变量带 local,公有静态变量不带local。
数据类型:
- nil 为空,无效值,在条件判断中表示 false
- boolean 包含两个值:false和true
- number 表示双精度类型的实浮点数
- string 字符串由一对双引号或单引号来表示
- function 由 C 或 Lua 编写的函数
- table Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型 thread 协程
- userdata 存储在变量中的C数据结构
print(type(signal)) --nil signal = true print(type(signal)) --boolean signal = 1454 print(type(signal)) --number signal = "UnionTech" print(type(signal)) --string signal = function() print(type(signal)) end print(type(signal)) --function signal = {} print(type(signal)) --table signal = coroutine.create(function() print(type(signal)) end) print(type(signal)) --coroutine
流程控制
if...elseif...else、 while、 for
--if...else ty_signal = type(signal) if ty_signal == "coroutine" then print("signal type is coroutine") elseif ty_signal == "table" then print("signal type is table") else print("signal type is other") end --while ut_companys = {"beijing company", "shanghai company", "nanjing company", "wuxi company", "guangzhou company", "yunfu company", "wuhan company", "chengdu company", "xian company"} count = 0 while count <= #ut_companys do count = count + 1 print("ut_companys[", count, "] is ", ut_companys[count]) end --for for i=#ut_companys, 1, -2 do --以2为步长反向遍历 print("num: ", i, "company: ", ut_companys[i]) end
table
表作为 Lua 唯一自带的数据结构, 使用简单方便, 兼具数组和Map作为容器的功能,通过表可以很容易组成常见的数据结构, 如栈、队列、链表、集合,用for循环很容易迭代遍历表数据。
--table当数组用,下标从1开始 for i, c in ipairs(ut_companys) do print(string.format("1 UnionTech company: %d %s", i, c)) end table.sort(ut_companys) for i=#ut_companys, 1, -1 do print(string.format("2 UnionTech company: %d %s", i, ut_companys[i])) end --table当hash map用 ut_cptypes = {} ut_cptypes["adapter"] = {"beijing company", "wuhan company", "guangzhou company"} ut_cptypes["developer"] = {"beijing company", "wuhan company", "nanjing company", "chengdu company", "xian company", "guangzhou company"} ut_cptypes["general"] = {"beijing company"} for ty, cps in pairs(ut_cptypes) do for i, cp in ipairs(cps) do print(string.format("3 UnionTech companys: type:%s identifier:%s company:%s", ty, i, cp)) end end
函数
在Lua中函数也是第一类型值, 可赋值给变量, 也可以在函数体内定义并使用函数,或者是直接使用匿名匿名函数。
--多重返回值 ut_types = {"adapter", "developer", "general"} function company_types(cp, cptypes) local adpt, dvlp, genl = nil, nil, nil for i, ty in ipairs(ut_types) do for _, _cp in ipairs(cptypes[ty]) do if _cp == cp then if i == 1 then adpt = true elseif i == 2 then dvlp = true elseif i == 3 then genl = true end break end end end return adpt, dvlp, genl end cp = "wuhan company" types = {company_types(cp, ut_cptypes)} for i, ty in ipairs(types) do if ty then print(string.format("%s is %s", cp, ut_types[i])) end end --变参 function printf(str, ...) print(string.format(str, ...)) end function add_companys(...) local newcps = {...} local num = #newcps for _, cp in ipairs(newcps) do table.insert(ut_companys, cp) end return ut_companys, num end _, _ = add_companys("changsha company", "zhengzhou company", "hefei company") for i=1, #ut_companys do --print(string.format("4 UnionTech company: %d %s", i, ut_companys[i])) printf("4 UnionTech company: %d %s", i, ut_companys[i]) end --闭包 function all_companys(cps) local companys, n = {}, 0 for _, v in ipairs(cps) do table.insert(companys, v) end return function() n = n + 1 if n > #companys then return "" else return companys[n] end end end get_company = all_companys(ut_companys) while true do cp = get_company() if cp == "" then break else printf("get company: %s", cp) end end
协程 Coroutine
Lua 协同程序 Coroutine 与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。
function foo (a) print("foo 函数输出", a) return coroutine.yield(2 * a) -- 返回 2*a 的值 end co = coroutine.create(function (a , b) print("第一次协同程序执行输出", a, b) -- co-body 1 10 local r = foo(a + 1) print("第二次协同程序执行输出", r) local r, s = coroutine.yield(a + b, a - b) -- a,b的值为第一次调用协同程序时传入 print("第三次协同程序执行输出", r, s) return b, "结束协同程序" -- b的值为第二次调用协同程序时传入 end) print("main", coroutine.resume(co, 1, 10)) -- true, 4 print("main", coroutine.resume(co, "r")) -- true 11 -9 print("main", coroutine.resume(co, "x", "y")) -- true 10 end print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine --resume将主协程数据传入次协程, yield将次协程中数据传回主协程
元表 Metatable
本质上来说就是存放元方法的表结构, 通过元表实现对表中数据和行为的改变。
Lua 查找一个表元素时的规则,其实就是如下 3 个步骤:
- 在表中查找,如果找到,返回该元素,找不到则继续
- 判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
- 判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值
father = { colourofskin = "yellow", weight = 70, work = "programming", otherwork = function() print "do housework" end } father.__index = father son = { weight = 50, like = "basketball" } setmetatable(son, father) printf("weight:%d like:%s work:%s colourofskin:%s ", son.weight, son.like, son.work, son.colourofskin) son.otherwork()
面向对象
因为lua本身不是面向对象的语言, 在lua中, 通过table和function来模拟一个对象, 用metatable来模拟面向对象中的继承,但是在使用的时候需要考虑lua作为脚本语言, 变量的类型随所赋值类型而改变。
--父类 rect = { area = 0, length = 0, width = 0, } function rect:getArea() if self.area == 0 then self.area = self.length * self.width end return self.area end function rect:getLength() return self.length end function rect:new(leng, wid) self.length = leng self.width = wid return self end --子类 cuboid = { volume = 0, height = 0, } function cuboid:getVolume() if self.volume == 0 then self.volume = self.height * self:getArea() end return self.volume end function cuboid:new(_rect, _height) setmetatable(self, _rect) _rect.__index = _rect self.height = _height return self end rect1 = rect:new(5, 10) print("rect1 rectangle:", rect1:getArea()) cuboid1 = cuboid:new(rect1, 2) print("cuboid1 volume: ", cuboid1:getVolume()) print("cuboid1 rectangle: ", cuboid1:getArea()) --子类调用父类方法getArea print("cuboid1 length function: ", cuboid1:getLength()) --子类调用父类方法getLength print("cuboid1 length variable: ", cuboid1.length) --子类使用父类变量length --重写子类接口getArea, lua中没有重载 function cuboid:getArea() return 2 * (self.height * self.length + self.height * self.width + self.length * self.width) end cuboid2 = cuboid:new(rect1, 2) print("cuboid2 function: getArea: ", cuboid2:getArea()) --调用子类重写的方法getArea print("cuboid2 base function: getArea: ", getmetatable(cuboid2):getArea()) --显示调用父类方法getArea
模块与 C 包
模块类似封装库, 有利于代码复用, 降低耦合, 提供被调用的 API。
-- 文件名为 module.lua, 定义一个名为 module 的模块 module = {} module.constant = "这是一个常量" function module.func1() io.write("这是一个公有函数!\n") end local function func2() print("这是一个私有函数!") end function module.func3() func2() end return module 在其他模块中调用module模块: local m = require("module") print(m.constant)
与 Lua 中写包不同,C 包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。Lua 在一个叫 loadlib 的函数内提供了所有的动态连接的功能。
lua 标准库
标准库中接口可直接使用不需要 require
常用标准库:
- math 数学计算
- table 表结构数据处理
- string 字符串处理
- os 系统库函数
- io 文件读写
- coroutine 协程库
- debug 调式器
lua 虚拟机
脚本语言没有像编译型语言那样直接编译为机器能识别的机器代码,这意味着解释性脚本语言与编译型语言的区别:由于每个脚本语言都有自己的一套字节码,与具体的硬件平台无关,所以无需修改脚本代码,就能运行在各个平台上。
硬件、软件平台的差异都由语言自身的虚拟机解决。由于脚本语言的字节码需要由虚拟机执行,而不像机器代码那样能够直接执行,所以运行速度比编译型语言差不少。
有了虚拟机这个中间层,同样的代码可以不经修改就运行在不同的操作系统、硬件平台上。Java、Python都是基于虚拟机的编程语言,Lua同样也是这样。
可在命令行 lua lua.lua 运行本脚本。