安装
linux 下安装方式
curl -R -O http://www.lua.org/ftp/lua-5.4.4.tar.gz
tar zxf lua-5.4.4.tar.gz
cd lua-5.4.4
# 编译并测试没有问题
make all test
make install
卸载
cd lua-5.4.4
# 删除相关配置,之后可以删除 lua-5.4.4
make uninstall
执行
文件以 .lua 结尾
方式一
# 先查找 lua 解释器的路径
which -a lua

 为 hello.lua 文件添加解释器路径
#!/usr/local/bin/lua
print("Hello Lua!")
添加可执行权限
chmod u+x ./hello.lua
运行
./hello.lua
方式二
lua hello.lua
注释
-- 单行注释
--[[
多行注释
--]]
--[[
多行注释
]]
--[=[
多行注释
--]=]
-- 取消多行注释,取消后注释中的代码可以执行
---[[
print("Hello Lua!")
--]]
交互
# 进入交互模式,或者 lua -i
lua
# 交互模式下退出,或者 ctrl+c 强制中断,或者 ctrl+D 发送退出信号
os.exit()

 
标识符
以字母、数字、下划线组成,数字不能开头
区分大小写
关键字
| if | else | elseif | nil | 
|---|---|---|---|
| return | while | break | end | 
| end | not | then | goto | 
| do | false | true | in | 
| or | function | for | local | 
| repeat | until | 
变量
全局变量
一般约定,以下划线开头连接一串大写字母的名字(如: _VERSION)被保留(或用于) lua 内部全局变量
默认情况下,变量总是全局的
-- 全局变量不需要声明,访问一个没有初始化的全局变量不会出错,结果默认为 nil
print(b) -- nil
b=10
print(b) -- 10
-- 删除全局变量
b=nil
print(b) -- nil

_G
保存全局环境的全局变量,改变它的值不会影响任何环境,只会影响自己的代码
--[[
	_G 不能使用 local 修饰
	local _ENV 可以指定局部环境变量 
--]]
function fn()
    -- 当前环境中只有 print 可用
    local _ENV = {print=_G.print}
    print(os)
    print(print)
end
fn()
-- 不影响
print(os)
局部变量
局部变量的作用域为从声明位置开始到所在语句块结束
局部变量的访问速度比全局变量更快
do
	local a=5 -- 使用 local 修饰
	print(a)
end
print(a)

赋值
lua 可以对多个变量同时赋值
a,b=1,2
print(a,b) -- 1 2
a,b=b,a
print(a,b) -- 2 1
数据类型
| 类型 | 描述 | 
|---|---|
| nil | 小写的(L),表示无效值 | 
| boolean | true、false | 
| number | 双精度的实浮点数 | 
| string | 双引号和单引号包含的内容,如: “lua” | 
| function | 函数 | 
| userdata | 用户自定义数据,将 C/C++ 的任意数据类型的数据(通常是 struct 和指针)存储到 lua 变量中 | 
| thread | 线程,用于执行 coroutine (协程) | 
| table | “关联数组”,索引可以是数字、字符串、表类型 | 
type
是一个函数,测定变量或值的类型
返回的是类型的字符串
print(type(a)) -- nil
print(type("lua")) -- string
print(type(1))	-- number
print(type(print)) -- function
print(type(true)) -- boolean
print(type(type(1))) -- string
print(type({})) -- table
string
一对单引号或双引号表示
a="lua"
b='lua'
使用 [[]] 或 [=[]=] 来表示多个行构成的字符串
s=[[
<html>
<head></head>
<body></body>
</html>
]]
字符串和数字进行算数操作, lua 会尝试将字符串转为数字进行运算
 
使用 .. 进行字符串的拼接
 
 使用 # 获取字符串长度,不能准确获取多字节构成的字符串的长度
 
table
table 通过"构造表达式"创建,一个简单的方式是使用 {} 来创建
local t1={} -- 空 table
local t2={1,2,"abc"} -- 可以存储多个类型
print(t2[1]) -- 默认下标从 1 开始

table 索引可以是字符串,可以看做 python 中的字典
a={}
a["name"]="tom"
a["age"]=18
for k,v in pairs(a) do
    print(k .. ":" .. v)
end
t={name="tom",age=18, "earth"}
-- 当索引是字符串时 [] 可以简化成 .
-- 当 table 的索引既有字符串,又有数字时,t[1]为第一个索引为数字的元素
print(t.name,t["name"], t[1]) -- tom tom earth
t=nil -- 删除
table 长度不固定,数据添加时会自动增长,可以考虑 java 中的 Map
function
函数可以作为变量
function cal(a, b, fn)
    return fn(a,b)
end
a=1
b=2
-- 可以使用匿名方式传参
print(cal(a,b, function(a,b) return a + b end))
utf8 支持
s = "abc他们"
-- 获取字符个数
print(utf8.len(s))
-- i 是字符在字节中开始的位置,v 是字符的码点
for i,v in utf8.codes(s) do
    print(i, v)
end
-- 获取 s 的全部码点
print(utf8.codepoint(s,1,-1))
-- 将码点转为字符串
print(utf8.char(97,98))
-- 返回第 4 个字符在字节中的开始处
print(utf8.offset(s,4))

运算符
算术运算符
a,b=4,2
-- 加法
print("a + b = "..a + b)
-- 减法
print("a - b = "..a - b)
-- 乘法
print("a * b = "..a * b)
-- 除法
print("a / b = "..a / b)
-- 取余
print("a % b = "..a % b)
-- 幂
print("a ^ b = "..a^b)
-- 负
print("-a = "..-a)
-- 整除
print("a // b = "..a//b)

关系运算符
| 操作符 | 说明 | 
|---|---|
| == | 等于,相等为 true, 不等为 false | 
| ~= | 不等于,不相等为 true, 相等为 false | 
| > | 大于 | 
| < | 小于 | 
| >= | 大于等于 | 
| <= | 小于等于 | 
逻辑运算符
nil 和 false 为假,其他为真,注意 0 也为真
not 将真变为假,将假变为真
右值为 boolean 时的运算
a,b = true,false
-- 逻辑与
print(a and b)
-- 逻辑或
print(a or b)
-- 逻辑非
print(not a)

 右值不全是 boolean 时的运算
a,b,c = 1,2,nil
print(a and b) -- a 为真,则返回 b,否则返回 a
print(c and a)
print(string.rep("-", 5))
print(a or b) -- a 为真,则返回 a,否则返回 b
print(c or a)
print(string.rep("-", 5))
print(not a) -- 真变为假
print(not c) -- 假变为真

按位运算符
a,b=8,2
print(a & b) -- 与
print(a | b) -- 或
print(a ~ b) -- 异或
print(~a)    -- 非
print(a >> b) -- 右移
print(a << b) -- 左移

其他运算符
-- .. 连接两个字符串
print("Hello ".."Lua")
-- # 一元运算符,返回字符串或表的长度
t={1,2,3}
print(#t)

运算符优先级
除 ^ 和 .. ,其他的二元运算符都是左连接的
| 操作符 | 优先级(1最高) | 
|---|---|
| ^ | 1 | 
| not - (负号) | 2 | 
| * / % // | 3 | 
| + -(减号) | 4 | 
| … | 5 | 
| < > <= >= ~= == | 6 | 
| and | 7 | 
| or | 8 | 
语句块
-- do...end 作为语句块
do
    local i = 5
    print(i)
end
print(i)

流程控制
if 语句
-- false 和 nil 为假
-- true 和 非nil 为真
-- 0 为 true
flag = true
if (flag) then
    print("true")
end
if-else 语句
flag = false
if (flag) then
    print("true")
else
    print("false")
end    
if-elseif 语句
score = 85
if (score <= 60) then
    print("不及格")
elseif (score <= 80) then
    print("良好")
elseif (score <= 90) then
    print("中等")
else
    print("优秀")
end    
if 嵌套
target = 92
if (target <= 100) then
    if (target >= 90) then
        print("在90-100范围")
    else
        print("小于90")
    end
else
    print("超出范围")
end
循环
循环可以嵌套使用
while 循环
local i = 0
while (i < 9) do
-- 输出
io.write(i,",")
i = i + 1
end
io.write(i, "\n")

无限循环
while (true) do
    print("一条信息")
end
for 循环
--[[
-- var 从 exp1 以 exp3 为步长增长到 exp2
-- 闭区间 [exp1,exp2]
-- exp3 可选,默认为1
-- exp1,exp2,exp3 在循环开始前一次性求值,以后不再进行求值
for var=exp1,exp2,exp3 do
	statement
end
--]]
for i=1,10,2 do
    -- 对于 i 的值的修改不影响循环次数
    print(i)
end
-- 使用 for 来输出 table
a = {"a", "b", "c", name="tom"}
-- ipairs 只对数字索引生效,遇到 nil 将会退出
for k,v in ipairs(a) do
    print(k..":"..v)
end

repeat…until 循环
与 C 语言的 do-while 类似
i = 5
repeat
    print(i)
    i = i - 1
until(i < 1)   

循环控制
break 跳出循环
i = 1
while (i < 10) do
	print(i)
    if (i > 4) then
    	print("跳出循环")
        break
    end
    i = i + 1
end

goto 跳转到指定标签处
--[[
goto label
-- label 格式
::Label::
--]]
i = 1
::A::
if (i < 5) then
    print(i)
    i = i + 1
    goto A
end
print("end")

数组
一维数组
arr = {4,5,2}
for i=1,#arr do
	print(arr[i])
end
多维数组
arr = {}
for i=1,2 do
	arr[i] = {}
    for j = 1, 2 do
       	arr[i][j] = j 
    end
end
for i=1,2 do
	for j=1,2 do
    	print(arr[i][j])
    end
end
函数
定义
scope function function_name(arg1,arg2,...)
	statement
	return statement
end
scope: 可选,指明函数是全局函数还是局部函数,默认为全局函数,局部函数取值为 local
function_name: 函数名
arg1,arg2: 参数,可以0个或多个
return statement: 可以返回多个值,用逗号 , 隔开
local function max(a,b)
	if (a > b) then
        return a
    else
        return b
    end
end
a,b=10,20
print(max(a,b))
函数作为右值
add = function(a, b)
    return a + b
end
print(add(1,2))
函数作为参数
function cal(a,b,fn)
	return a,fn(a,b)
end
a,b=cal(10,20,function(a,b) return a + b end)
print(a, b)

函数作为返回值
function linear(k)
	return function(x,b)
    	return k * x + b 
    end
end
fn = linear(2)
print(fn(1,1))
print(fn(2,2))

可变参数
使用 ... 表示可变参数,固定参数必须放在可变参数之前
function fn(...)
    -- {...} 选取可变参数中非 nil 的参数构成的数组
	for k,v in pairs({...}) do
    	io.write(v.." ")
    end
    print()
end
fn(1,2,nil,4,nil,nil)

使用 # 获取可变参数的长度,存在 nil 时不准确
function fn(...) 
	print("可变参数个数: "..#{...})
end
fn(1,2,3,nil,5,nil)

使用 select 获取可变参数的长度
function fn(...)
    -- 可以处理出现 nil 的情况
	print("可变参数个数: "..select("#", ...))
    --[[
    	select(n,...) 返回从 n 开始到最后的所有的参数列表
    	n 从 1 开始,也可以取负值,-1表示最后一个参数
    --]]
    a,b,c,d = select(1,...)
    print(a,b,c,d)
    print(select(1,...))
end
fn(1,2,3,nil,5,nil)

next
--[[
	-- index 为索引默认为 nil
	-- 函数返回表的下一个索引和值,将 index 取值为该索引可以获取下一个索引和值
	-- 返回的索引和值的顺序不固定
	-- index 为 nil 时,返回一个初始索引和值
	-- 可以判断表是否为空
	next(table[,index])
--]]
t = {name="tom",age=18,20}
k,v=next(t)
while(true) do
	if (k) then
        print(k,v,t[k])
        k,v=next(t,k)
    else
        break
    end
end

assert
--[[
	-- v 为 false 或 nil 时断言失败
	-- message 是字符串,断言失败的信息
	-- 断言成功返回所有参数
	assert(v[,message])
--]]
print(assert(1,"断言成功"))

tonumber
--[[
	-- base 取值 [2,36],指定 e 的进制,默认 10
	tonumber(e[,base])
--]]
print(tonumber("16"))
print(tonumber("16",16))

tostring
--[[
	-- 将任意类型 v,转为字符串
	tostring(v)
--]]
print(tostring(1), type(tostring(1)))
print(tostring(nil), type(tostring(nil)))
print(tostring({name=tom,10}), type(tostring({name=tom,10})))

pcall
--[[
	-- 在保护模式下使用给定参数调用函数 f,函数 f 中的错误不会传播
	-- pcall 捕获错误并返回状态代码
	-- 返回的第一个值是状态码 true 或 false
	-- 状态码是 true,还会返回所有调用结果
	-- 状态码是 false,还会返回 error 对象
	pcall(f[,arg1,...])
--]]
f,t = pcall(assert, false, "断言失败")
print(f,t)

xpcall
提供一个错误处理函数,可以获取更多的调试信息
f, t = xpcall(assert, function (err)
    print(err)
    return debug.traceback()
end, 1 == 2 , "断言失败")
print(f,t)

dofile
打开命名文件并将其内容作为 Lua 块执行。当不带参数调用时,dofile 执行标准输入 stdin 的内容。返回该块返回的所有值。如果出现错误,dofile 将错误传播给它的调用者。也就是说,dofile 不会在保护模式下运行。
--[[
	dofile([filename])
--]]
load
--[[
	-- chunk 最终是一个字符串
	-- 没有语法错误,load 返回一个函数,否则返回 nil 和 错误信息
	-- chunkname 用作错误消息和调试信息的块的名称
	-- mode:"b",只是二进制块;"t",只是文本块;"bt",文本和二进制块。默认为 "bt"
	-- env 是 _ENV 传入一个 table
	-- 运行恶意制作的字节码会使解释器崩溃
	load(chunk[,chunkname[,mode[,env]]])
--]]
local f,msg = load([[print("tom")]], "运行错误","bt",{print=_G.print})
pcall(f)

loadfile
类似于 load,但如果没有给出文件名,则从标准输入获取块。
--[[
	loadfile([filename[,mode[,env]]])
--]]
table
concat
--[[
-- 将表 list 从 list[i] 到 list[j] 以字符串 sep 连接 
table.concat(list,sep,i,j)
--]]
t = {"a", "b", "c"}
print(table.concat(t, "-", 1, 3))

insert
--[[
	-- 在指定的 list[pos] 位置插入 value 
	table.insert(list, pos, value) 
--]]
t = {"a", "b", "c"}
table.insert(t, 1, "d")
for k, v in pairs(t) do
    print(v)
end

remove
--[[
    -- 删除 list[pos] 位置元素
    table.remove(list, pos)
--]]
t = {"a", "b", "c", "d"}
-- 默认删除最后一个
table.remove(t)
for k, v in pairs(t) do
    print(v)
end
print(string.rep("=", 5))
table.remove(t, 2)
for k, v in pairs(t) do
    print(v)
end

sort
--[[
    -- 按照 comp 函数进行排序
    table.sort(list, comp)
--]]
a = {3, 10, 8, 2, 4}
table.sort(a, function (a,b)
    return a > b
end)
for k,v in pairs(a) do
    print(v)
end

pack
构建 table
function fn(...) 
    -- pack 将非 nil 的值构建成 table,并添加一个 k=v,其中 k 是 n,v 是值的全部个数
    t = table.pack(...)
    for k,v in pairs(t) do
    	print(k..":"..v)
    end
end
fn(1,2,3,nil,5,nil,nil)

unpack
--[[
	-- 返回表 list[i] 到 list[j] 中的元素
	table.unpack(list,i,j)
--]]
list = {1,2,3,nil,5,nil,nil,6,nil}
-- 从 list[1] 到 list[j] 的元素,list[j]是从右向左第一个不为 nil 的元素
print(table.unpack(list))
-- list[2] 到 list[7] 的元素
print(table.unpack(list,2,7))

获取表长
-- 使用该种方式构建 table 时, 用 # 方式获取表长,当最后的元素为 nil 时,会导致长度不准确 
list = {nil,1,2,3,nil,5,nil,nil,6,nil}
-- # 获取的长度是 list[1] 到 list[j] 的个数,list[j]为从右向左第一个不为 nil 的元素
print(#list)

 使用 pack 方式构建可以解决上述问题
-- pack 构建 table 时,添加了键 n,该键的值是 table 的表长
list = table.pack(nil,1,2,3,nil,5,nil,nil,6,nil)
-- 表长
print(list.n) -- list["n"]
for i=1,list.n - 1 do
	io.write(list[i] or "nil","\t")
end
io.write(list[list.n] or "nil")
print("")

table 中 function 的使用
function sub(a,b)
	return a - b
end
t = {add=function(a,b) return a + b;end,sub=sub}
-- print(t["add"](1,2))
print(t.add(1,2))
print(t.sub(1,2))
迭代器
ipairs
第一次遇到 nil 将停止迭代
t = {1,2,3,nil,4,5}
for k,v in ipairs(t) do
    print(k..":"..v)
end

pairs
遇到 nil 不会停止迭代
t = {1,2,3,nil,4,5}
for k,v in pairs(t) do
    print(k..":"..v)
end

元表与元方法
元表(metatable),可以改变 table 的行为,每个行为关联了对应的方法(元方法)
| 元方法 | 说明 | 
|---|---|
| __add | 运算符 + | 
| __sub | 运算符 - | 
| __mul | 运算符 * | 
| __div | 运算符 / | 
| __idiv | 运算符 // | 
| __mod | 运算符 % | 
| __unm | 运算符负号 - | 
| __concat | 运算符 … | 
| __eq | 运算符 == | 
| __lt | 运算符 < | 
| __le | 运算符 <= | 
| __band | 按位与运算,如: 1 & 2 | 
| __bor | 按位或运算,如: 1 | 2 | 
| __bxor | 按位异或运算,如: 1 ~ 2 | 
| __bnot | 按位非运算,如: ~ 1 | 
| __shl | 按位左移运算,如: 1 << 2 | 
| __shr | 按位右移运算,如: 8 >> 2 | 
| __len | # 操作 | 
| __index | 访问表 | 
| __newindex | 对表进行更新 | 
| __call | 让表像函数一样调用 | 
| __tostring | 修改表的输出行为 | 
setmetatable
对指定的 table 设置元表,若元表中存在 __metatable 键值,setmetatable 会失败
--[[
	setmetatable(table,metatable)
--]]
list = {1,2,3,nil,5,nil,nil,6,nil}
t = {}
-- 返回 list
t1 = setmetatable(list, t)
print(table.unpack(t1))
getmetatable
返回指定 table 的元表
list = {1,2,3,nil,5,nil,nil,6,nil}
t = {10,nil,nil,11}
setmetatable(list, t)
m = getmetatable(list)
print(table.unpack(m))

rawset 和 rawget
t = {name="tom"}
t1 = {score=99}
setmetatable(t,t1)
print(rawget(t, "name")) -- 不对元表操作,只获取表 t 中的索引值,不存在返回 nil
print(rawget(t, "score"))
rawset(t, "address", "earth") -- 不调用元表中的方法,只更新表 t
print(t.address)
print(t1.address) -- 元表中不存在

rawequal
--[[
	-- 语法,判断 v1 和 v2 是否相等,不调用 __eq 元方法,返回 true 或 false
	-- v1,v2 任意类型
	rawequal(v1,v2)
--]]
print(rawequal(1,1))
print(rawequal(1,2))
print(rawequal(1,nil))
print(rawequal(1,"1"))

rawlen
--[[
	-- 语法,v 只能是字符串和 table,返回 v 的长度,不调用任何元方法
	rawlen(v)
--]]
print(rawlen("hello"))
print(rawlen({1,2,nil,3}))

__index
通过索引访问 table 时,若该索引没有值,则寻找该 table 对应的 metatable 中的 __index中的索引
t = {name="tom"}
setmetatable(t,{__index=function(t,k) if k == "age" then return 18; end end})
print(t.name)
-- 访问元表
print(t.age)

将函数简化成 table, 形式更为简洁
t = {name="tom"}
setmetatable(t,{__index={age=18, address="earth"}})
print(t.name)
print(t.age) -- 访问元表
print(t.address)
print(t.score) -- 元表中也不存在

__newindex
给指定表的不存在的索引赋值时,会查找 __newindex 元方法,并将键值存在 __newindex 值中
t = {name="tom"}
t1 = {}
setmetatable(t,{__newindex=t1})
t.address="earth" 
print(t.address) -- 表中不存在
print(t1.address)
print(string.rep('-',5))
t.score=89 
print(t.score) 
print(t1.score)
print(string.rep("-",5))
for k,v in pairs(t) do
    io.write(k..":"..v," ")
end
print("")
for k,v in pairs(t1) do
    io.write(k..":"..v," ")
end
print("")

 利用 __newindex 为指定表更新值
t = {name="tom"}
t1 = {}
setmetatable(t,{
    __newindex=function(t, index, value)
        -- rawset 不会调用 `__newindex` 元方法,并为表 t 设置值
        -- 不使用该方法,直接 t[index] = value 会调用 __newindex,导致死循环    
        rawset(t, index, value)
    end
})
t.name="jerry" -- t 中有 name, 直接更新 t,不调用元方法
print(t.name) 
print(string.rep("-",5))
t.age=18 -- t 中没有 age,查找 __newindex 元方法
print(t.age) 
print(t1.age)
print(string.rep("-",5))
t.score=89 
print(t.score) 
print(t1.score)
print(string.rep("-",5))
for k,v in pairs(t) do
    io.write(k..":"..v," ")
end
print("")
-- 修改了 __newindex,所以 t1 中不存在值 
for k,v in pairs(t1) do
    io.write(k..":"..v," ")
end
print("")

__tostring
修改 table 的输出行为
t = {1,2,3,nil,10}
setmetatable(t,{
    __tostring=function(t)
        s = t[1] or "nil"
        for i=2,#t do
            s = s..","..(t[i] or "nil")
        end
        return s
    end
})
print(t)

__call
让表像函数一样调用
t = {1,2,3,nil,10}
setmetatable(t,{
    __call=function(t,t1,t2)
        print(t1,t2)
        s = 0
        for i=1,#t do
            s = s + (t[i] or 0)
        end
        return s
    end
})
-- 对 t 进行求和
print(t(1,2))

__add
让表具有 + 的功能
t = {1,2,3,nil,10}
t1 = {11,nil,9,8}
setmetatable(t,{
    __add=function(t,t1)
        t2 = {}
        for i=1,#t do
            t2[i] = t[i]
        end
        len = #t
        for i=1,#t1 do
            t2[len + i] = t1[i]
        end
        return t2
    end
})
-- 将 t 和 t1 中的元素合并,并返回一个新的表
t2 = t + t1
print(table.unpack(t2))

__unm
t = {1,2,3}
t1 = {
    __unm = function(t)
        for i=1,#t do
            if (type(t[i]) == "number") then
                t[i] = -t[i]
            end
        end
        return t
    end
}
setmetatable(t,t1)
print(table.unpack(-t))

面向对象
. 和 : 定义函数
 
. 和 : 定义函数的区别在于 : 会提供一个隐含的 self 参数,该参数是当前的 table
Person = {name="tom", age=18}
--[[
	-- 与下面的等价
	Person.getName = function() 
		return Person.name
	end
--]]
function Person.getName()
    return Person.name
end
--[[
	-- 与下面等价,注意调用时使用的是 Person:getAge()
	-- self 只是参数名,可以替换成其他的参数名,如:s,一般使用 self
	-- : 方式调用,lua 会将当前表作为参数传递给第一个参数
	Person.getAge = function(self)
		return self.age
	end
	-- 该种方式传参,调用方式 Person:setScore(98)
	-- . 方式调用,需要手动传入,Person.setScore(Person,99)
	Person.setScore = function(self,score) 
		self.score = score
		print(self.score)
	end
--]]
-- 这种方式定义, lua 会自动传入一个参数名为 self 的参数,指代 table,注意其他参数名不要与之相同
function Person:getAge()
    print(self,Person)
    return self.age
end
-- 怎么定义的怎么调用,推荐使用上面非注释的方式定义
print(Person.getName())
print(Person:getAge())

创建对象
Person = {name="name",age=0}
function Person:new() 
	o = {}
    -- 设置自索引,当 o 访问的索引不存在时,将会查找元表
    self.__index = self
    -- 为 o 设置元表,放在所有元方法之后
    setmetatable(o, self)
    return o
end
p = Person:new()
p1 = Person:new()
p.name = "tom"
p.age = 18
print(p.name,p.age)
print(p1.name, p1.age)

继承和重写
Person = {
    name="name",
    age=0
}
Person.__index = Person
function Person:work()
    print("工作")
end
Student = {}
setmetatable(Student, Person)
-- 继承
print(Student.name, Student.age)
Student:work()
print(string.rep("-",5))
-- 重写
function Student:work()
    print("学习")
end
Student:work()

多重继承
注意添加 __index 即可
成员私有化
function createPerson()
    local name = ""
    local age = 0
    local function getName()
        return name
    end
    local function getAge()
        return age
    end
    local function setName(n_name)
        name = n_name
    end
    local function setAge(n_age) 
        age = n_age
    end
    -- 提供指定的方法获取和修改属性
    return {
        getName=getName,
        getAge = getAge,
        setName=setName,
        setAge=setAge
    }
end
p = createPerson()
p.setName("tom")
p.setAge(18)
print(p.getName(),p.getAge())
coroutine
拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协程共享全局变量和其它大部分东西。
在任一指定时刻只有一个协程在运行,并且这个正在运行的协程只有在明确的被要求挂起的时候才会被挂起。
语法
| coroutine.close(co) | 关闭协程 co,即关闭其所有待关闭的变量,并将协程置于 "dead"状态。给定的协程必须已死或挂起。如果出现错误,则返回false和错误对象;否则返回true。 | 
| coroutine.create(f) | 创建一个新的协程,f 必须是一个函数。返回一个新的协程,一个类型为 "thread"的对象。 | 
| coroutine.isyieldable([co]) | 当协程 co 可以挂起时返回 true。co 的默认值是正在运行的协程。 | 
| coroutine.resume(co [, val1, ···]) | 开始或继续协程的执行。当第一次恢复一个协程时,它开始运行它的主体。值 val1,...作为参数传递给函数。如果协程已经挂起,将重新启动它;值val1,...将作为yield的返回结果。如果协程运行时没有任何错误,resume返回true和yield的参数值或函数返回的任何值(当协程终止时)。如果有任何错误,resume返回false和错误消息。 | 
| coroutine.running() | 返回正在运行的协程和一个布尔值,当运行的协程是主协程时为 true。 | 
| coroutine.status(co) | 返回协程的状态: "running"、"suspended"、"dead"、"normal" | 
| coroutine.wrap(f) | 创建一个新的协程,f 必须是一个函数。返回一个函数,该函数在每次调用协程时恢复协程。传递给该函数的任何参数都充当要继续的额外参数。该函数返回与 resume返回的值相同的值,除了第一个布尔值。如果发生错误,该函数关闭协程并传播错误。 | 
| coroutine.yield(···) | 暂停调用协程的执行。 yield的任何参数都作为resume的返回结果。 | 
示例
第 i 次 resume 返回的是第 i 次 yield 的参数,第 i 次 yield 返回的是第 i+1 次 resume 的参数
function fn(a,b)
    print("fn 调用 yield 前")
    -- 第一次 yield
    local x,y,z = coroutine.yield(a * 10,b * 10,2,3,4)
    print("第一次 yield 返回来自第二次 resume 的参数值:",x,y,z)
    print("fn 调用 yield 后")
    return "fn end"
end
co = coroutine.create(function (a,b)
    -- 第二次 resume 之后,函数 fn 中 没有其他的 yield,故执行 return  
    local r = fn(a,b)
    print("函数 fn 的返回值:",r)
    -- 第二次 yield
    local x,y = coroutine.yield(7,8)
    print("第二次 yield 返回来自第三次 resume 的参数值:",x,y)
    return "结束协程"
end)
-- 第一次 resume 返回的是第一次 yield 的参数值 
print("第一次 resume:",coroutine.resume(co,1,2))
-- 第二次执行 resume 将第一次执行 yield 的挂起转为运行,并将 resume 参数作为第一次 yield 的返回值
-- 同时 resume 将第二次 yield 的参数作为返回值
print("第二次 resume:",coroutine.resume(co, 3, 4,5))
-- 第三次 resume 时,已经没有 yield 且函数有返回值,故将函数的返回值作为 resume 的返回值
-- 并且结束协程
print("第三次 resume:",coroutine.resume(co, 5,6))
-- 第四次 resume,协程已经结束,故返回 false 和 错误信息
print("第四次 resume:",coroutine.resume(co, 5,6))

文件
io 方法
open
打开文件
语法
-- mode 可选
f = io.open(filename[,mode])
mode 值
 
| 模式 | 说明 | 
|---|---|
| r | 只读方式打开文件,该文件必须存在,默认模式 | 
| w | 打开只写文件,若文件存在,则内容为空,文件不存在,则新建 | 
| a | 以附加的方式打开只写文件,文件不存在,则新建,文件存在,则在文件尾部追加内容 | 
| r+ | 以可读写方式打开文件,文件必须存在 | 
| w+ | 打开可读写文件,若文件存在,则内容为空,文件不存在,则新建 | 
| a+ | 以附加的方式打开可读写文件,文件不存在,则新建,文件存在,则在文件尾部追加内容 | 
| b | 二进制方式打开文件 | 
| + | 对文件既可读也可写 | 
close
关闭文件
语法
-- file 不存在,关闭默认的文件
io.close([file])
flush
向文件写入缓冲区的内容
语法
io.flush()
input
当使用文件名调用时,它将打开命名文件(以文本模式),并将其句柄设置为默认输入文件。当使用文件句柄调用时,它只是将该文件句柄设置为默认输入文件。当不带参数调用时,它将返回当前默认输入文件。如果发生错误,此函数将引发错误,而不是返回错误代码。
语法
io.input([file])
output
与 input 类似,不同的是 output 设置输出文件
语法
io.output([file])
write
将内容写入缓冲区
语法
-- 参数只能是 stirng 和 number
io.write(...)
read
根据指定要读取内容的给定格式读取文件文件。对于每种格式,该函数返回已读字符的字符串或数字,如果不能读取指定格式的数据,则返回失败。读取失败,将不会继续读取。当不带参数调用时,它使用默认格式读取下一行。
语法
io.read(...)
格式
l(小写的 L) 和 L 只能用于文本文件
| 格式 | 说明 | 
|---|---|
| “n” | 遵循 Lua 的词法约定,读取数字并将其作为浮点数或整数返回。数字可以有前导空格和符号。这种格式总是读取最长的输入序列,这是一个数字的有效前缀;如果该前缀没有形成有效的数字(例如,空字符串, 0x或3.4e-)或它太长(超过200个字符),它将被丢弃,格式返回失败。 | 
| “a” | 从当前位置开始读取整个文件。在文件结束时,它返回空字符串;这种格式永远不会失败。 | 
| “l” | 读取下一行,跳过行尾,在文件结束时返回失败。这是默认格式。 | 
| “L” | 读取下一行,保留行结束符(如果存在),在文件结束时返回 nail。 | 
| number | 读取不超过这个字节数的字符串,在文件结束时返回 nail。如果 number 为 0,则不读取任何内容并返回空字符串,或在文件结束时失败。 | 
示例
f = io.open("./test.txt", "a+")
io.input(f)
while (true) do
    s = io.read()
    if (s) then
        print(s)
    else
        break
    end
end
io.close(f)

lines
以读模式打开给定的文件名,并返回一个迭代器函数。当迭代器函数无法读取任何值时,它会自动关闭文件。除了迭代器函数,io.lines 返回另外三个值:两个空值作为占位符,加上创建的文件句柄。因此,当在泛型for循环中使用时,如果循环中出现错误或被 break 中断,文件也将关闭。io.lines() 默认遍历输入文件的行,迭代器在循环结束时不会关闭文件。如果打开文件时出现错误,此函数将引发错误,而不是返回错误代码。
语法
-- filename 是文件名, ... 与 io.read(...) 中的一致
io.lines([filename,...])
popen
打开文件,此功能依赖于系统,并非在所有平台上都可用,推荐使用 open
tmpfile
如果成功,返回一个临时文件的句柄。此文件以 w+ 或 r+ 打开,并在程序结束时自动删除。
语法
io.tmpfile()
type
检查文件是否是有效的文件句柄。若是一个打开的文件句柄,则返回字符串 "file";若是一个关闭的文件句柄,则返回字符串 "closed file";若不是一个文件句柄,则返回 nil。
语法
io.type(file)
示例
-- test.txt 存在
f = io.open("./test.txt")
print(io.type(f),type(io.type(f)))
io.close(f)
print(io.type(f),type(io.type(f)))
-- 不是一个文件句柄
print(io.type("tt"),type(io.type("tt")))

file 方法
close
关闭文件,当文件句柄被垃圾回收时,文件会自动关闭。
语法
file:close()
flush
将缓冲区的内容写入文件
语法
file:flush()
lines
与 io.lines(...) 类似。不同的是,在循环结束时,该方法不会关闭文件。
语法
-- ... 与 io.read(...) 中的一致
file:lines(...)
read
与 io.read(...) 等价
语法
file:read(...)
write
与 io.write(...) 等价
语法
file:write(...)
seek
从 whence 处开始,offset 作为偏移读取文件。offset 是 number,whence 是字符串。如果失败,则返回 nil。默认是 whence 为 "cur",offset 为 0
| whence | 说明 | 
|---|---|
| “set” | 文件开始位置 | 
| “cur” | 文件当前位置 | 
| “end” | 文件结束位置 | 
语法
file:seek([whence[,offset]])
setvbuf
设置文件缓冲区的模式,size 是缓冲区大小,以字节为单位。模式依赖于平台。
| mode | 说明 | 
|---|---|
| “no” | 无缓冲区 | 
| “full” | 全缓冲 | 
| “line” | 行缓冲 | 
语法
file:setvbuf(mode[,size])
简单模式
f = io.open("./test.txt", "a+")
io.input(f)
print(io.read())
io.close(f)
完全模式
使用 file 方法代替 io 方法
f = io.open("./test.txt", "a+")
-- 输出第一行内容
print(f:read())
f:close()
模块
定义一个模块
-- net.lua
net = {}
local local_ip = "127.0.0.1"
local local_port = 8080
function net.ip()
    return local_ip
end
function net.setIP(ip)
    local_ip = ip
end
function net.port()
    return local_port
end
function net.setPort(port)
    local_port = port
end
return net
加载模块
-- test.lua 与 net.lua 在同一路径下
net = require("net")
print(net.ip())
net.setPort(8081)
print(net.port())
package.path
加载模块时查找的路径
print(package.path)
--[[
	假定文件以如下方式存在
	|_test.lua
	|_lib
		|_net.lua
	在 test.lua 中加载 net.lua
	net = require("./lib/net")
--]]
-- 第二种方式
package.path=package.path..";./lib/?.lua"
net = require("net")
print(net.ip())
-- 第三种方式:将 net.lua 文件放在 package.path 查找的路径目录下
调用 C 库
-- 可以通过 package.cpath 来修改加载路径
local path = "./net.so"
-- package.loadlib(libname,funcname) 动态加载库
-- funcname 必须遵循 lua_CFunction 规定
local f = assert(package.loadlib(path, "ip"))
-- 调用库
f()



















