--0、Lua的保留关键字
--[[
and break do else
elseif end false for
function if in local
nil not or repeat
return then true until
while
--]]
--1、输出
--直接输出
print("Hello World")
--2、注释
--单行注释
--[[
多行注释
--]]
--3、全局变量
--未初始化的全局变量
print(a) --> nil
--初始化的全局变量
b = 10
print(b) --> 10
--删除全局变量
b = nil
print(b) --> nil
--4、Lua数据类型
--[[
Lua是动态类型语言,变量不要类型定义,只需要为变量赋值。
值可以存储在变量中,作为参数传递或结果返回。
Lua中有8个基本类型分别为:
nil——只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)
boolean——包含两个值:false和true
number——双精度类型的实浮点数
string——字符串由""或''来表示
userdata——表示任意存储在变量中的C数据结构
function——由 C 或 Lua 编写的函数
thread——表示执行的独立线路,用于执行协同程序
table——表示一个"关联数组"(associative arrays),
数组的索引可以是数字或者是字符串。在 Lua 里,
table的创建是通过"构造表达式"来完成,最简单
构造表达式是{},用来创建一个空表。
--]]
print(type("Hello world")) --> string
print(type(10.4*3)) --> number
print(type(print)) --> function
print(type(type)) --> function
print(type(true)) --> boolean
print(type(nil)) --> nil
print(type(type(X))) --> string
--[[
(1)、nil(空):表示一种没有任何有效值,它只有一个值nil。
给全局变量或者table表里的变量赋一个nil值,等同于删掉
--]]
--(2)、boolean
if false or nil then
print("至少有一个是 true")
else
print("false 和 nil 都为 false!")
end --> false 和 nil 都为 false!
--(3)、number: 原样输出
print(type(7.8263692594256e-06)) --> 7.8263692594256e-06
--(4)、string
print("this is s1") --> this is s1
print('this is s2') --> this is s2
--"[[]]"表示一块字符串,原样输出
html = [[
菜鸟教程
]]
print(html)
--[[
在对“数字字符串”进行算术操作时,Lua 会尝试将它转成“数字”;
非数字字符串会报错
--]]
print("2" + 6) --> 8
print("2" + "6") --> 8
print("2 + 6") --> 2+6
print("-2e2" * "6") --> -1200
--字符串连接使用的是".."
print("a" .. 'b') --> ab
print(157 .. 428) --> 157428
print("error" .. 1) --> error1
--“#”放在字符串前面,计算字符串的长度
print(#"www.runoob.com") --> 14
--(5)、table
--创建一个空表
local tb1 = {}
--直接初始化表
local tb2 = {"apple", "pear", "orange", "grape"}
-- "关联数组",数组的索引可以是数字或者是字符串。
a = {}
a["key"] = "value"
key = 10
a[key] = 22
a[key] = a[key] + 11
for k, v in pairs(a) do
print(k .. " : " .. v)
end -->key : value
-->10 : 33
--table默认初始索引一般以 1 开始
t1 = {k1 = "1", k2 = 2, "3", 4}
for k,v in pairs(t1) do
print(k .. " - " .. v)
end --> 1 - 3
--> 2 - 4
--> k1 - 1
--> k2 - 2
t1.k2 = nil
for k,v in pairs(t1) do
print(k .. " - " .. v)
end
--[[
table不会固定长度大小,有新数据添加时table长度会自动增长,
没初始的 table 都是 nil
--]]
a3 = {}
for i = 1, 10 do
a3[i] = i
end
for k,v in pairs(a3) do
print(k .. " - " .. v)
end --> 输出1~10的索引及值
a3["key"] = "val"
print(a3["key"]) --> val
print(a3["none"]) --> nil
--[[
(6)、function(函数):Lua中,函数是被看作是
"第一类值(First-Class Value)",函数可以存在变量里
--]]
function factorial1(n)
if n == 0 then
return 1
else
return n * factorial1(n - 1)
end
end
print(factorial1(5)) --> 120
factorial2 = factorial1
print(factorial2(5)) --> 120
--function 可以以“匿名函数”(anonymous function)的方式通过参数传递
function testFun(tab, fun)
for k, v in pairs(tab) do
print(fun(k, v));
end
end
tab = {key1="val1",key2="val2"};
testFun(tab,
function(key, val) -- 匿名函数
return key.."="..val;
end
); --> key1=val1
--> key2=val2
--[[
(7)、在Lua里,最主要的线程是协同程序(coroutine)。
它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,
可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:
线程可以同时多个运行,而协程任意时刻只能运行一个,
并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
--]]
--[[
(8)、userdata 是一种用户自定义数据,用于表示一种由应用程序
或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型
的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
--]]
--5、Lua变量
--[[
(1)、变量在使用前,必须在代码中进行声明,即创建该变量。
编译程序执行代码之前,编译器需要知道“如何给语句变量开辟存储区”,
用于存储变量的值。
Lua 变量有三种类型:全局变量、局部变量、表中的域。
Lua 中的变量全是全局变量,那怕是语句块或是函数里,
除非用 local 显式声明为局部变量。
局部变量的作用域:从声明位置开始到所在语句块结束。
变量的默认值均为 nil。
--]]
a = 5 --全局变量
local b = 5 --局部变量
function joke()
c = 5 --全局变量
local d = 6 --局部变量
end
joke()
print(c,d) --> 5 nil
do
local a = 6 -- 局部变量
b = 6 -- 全局变量
print(a,b); --> 6 6
end
print(a,b) --> 5 6
--(2)、赋值语句
--赋值是改变一个变量的值和改变表域的最基本的方法。
a = "hello" .. "world"
t={n = 1, "2"}
t.n = t.n + 1
--[[
Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用“,”分开,
赋值语句右边的值会依次赋给左边的变量。
--]]
x = 1
a, b = 10, 2*x -- a=10; b=2*x
--[[
遇到赋值语句,Lua会先计算右边所有的值,
然后再执行赋值操作,So可以进行“交换”变量的值
--]]
x, y, a = 1, 2, {1, "2"}
x, y = y, x -- swap 'x' for 'y'
i, j = 1, 2
a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[j]'
print(x..y..a[i]..a[j]) --> 2121
--[[
当变量和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
a. 变量个数 > 值的个数 按变量个数补足nil
b. 变量个数 < 值的个数 多余的值会被忽略
--]]
a, b, c = 0, 1
print(a,b,c) --> 0 1 nil
a, b = a+1, b+1, b+2 -- value of b+2 is ignored
print(a,b) --> 1 2
a, b, c = 0 -- 若多变量赋值,要a, b, c = 0, 0, 0
print(a,b,c) --> 0 nil nil
--多值赋值经常用来“交换变量”,或将函数调用返回给变量
function max(a)
local m = a[1];
local mIndex = 1;
for i, v in ipairs(a) do
if v > m then
m = v;
mIndex = i;
end
end
return mIndex, m
end
a = {1, -23, 34, 0, 8}
a, b = max(a)
print(a.." = "..b) --> 3 = 34
--[[
max()返回两个值,第一个赋给a,第二个赋给b。
应该尽可能的使用局部变量,有两个好处:
1. 避免命名冲突。
2. 访问局部变量的速度比全局变量更快。
--]]
--[[
(3)、索引:table索引使用“[]”或“.”
t[i]
t.i -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问,本质上是一个类似这样的函数调用
--]]
--6、Lua循环:循环语句是由循环体及循环的终止条件两部分组成的。
--(1)、while循环
--[[
while(condition)
do
statements
end
statements(循环体语句) 可以是一条或多条语句,condition(条件)
可以是任意表达式,在condition(条件)为true时执行循环体语句。
--]]
a=15
while( a < 20 )
do
print("a 的值为:", a)
a = a+1
end
--(2)、for循环
--[[
for循环语句可以重复执行指定语句,重复次数可在for语句中控制。
for语句有两大类:数值for循环和泛型for循环
1)、数值for循环
for var=exp1,exp2[,exp3] do
<执行体>
end
var从exp1变化到exp2,每次变化以exp3为步长递增var,
并执行一次"执行体"。exp3是可选的,如果不指定,默认为1
2)、泛型for循环
泛型for循环通过一个迭代器函数来遍历所有值,类似java中的foreach语句。
--打印数组a的所有值
for i,v in ipairs(a)
do print(v)
end
i是数组索引值,v是对应索引的数组元素值。
ipairs是Lua提供的一个迭代器函数,用来迭代数组。
--]]
for i=10,1,-1 do
print(i)
end
days = {"Sua","Mon","Tue","Wed","Thu","Fri","Sat"}
for i,v in ipairs(days) do print(v) end
--(3)、repeat ... util
--[[
repeat...until循环的条件语句在当前循环结束后判断
repeat
statements
until(condition)
--]]
--变量定义
a = 10
--执行循环
repeat
print("a的值为:", a)
a = a + 1
until( a > 5 )
--(4)、循环嵌套:允许循环中嵌入循环
--[[
1)、for 循环嵌套语法格式:
for init,max/min value, increment
do
for init,max/min value, increment
do
statements
end
statements
end
2)、while 循环嵌套语法格式:
while(condition)
do
while(condition)
do
statements
end
statements
end
3)、 repeat...until 循环嵌套语法格式:
repeat
statements
repeat
statements
until( condition )
until( condition )
也可以使用不同的循环类型来嵌套,如 for 循环体中嵌套 while 循环
--]]
j =2
for i=2,10 do
for j=2,(i/j) , 2 do
if(not(i%j))
then
break
end
if(j > (i/j))then
print("i 的值为:",i)
end
end
end
--> i 的值为: 8
--> i 的值为: 9
--> i 的值为: 10
--(5)、循环控制语句
--break 语句:退出当前循环或语句,并开始脚本执行紧接着的语句。
--定义变量
a = 10
--while循环
while( a < 15 )
do
print("a 的值为:", a)
a=a+1
if( a > 13)
then
--使用 break 语句终止循环
break
end
end
--(6)、无限循环:在循环体中如果条件永远为 true 循环语句就会永远执行下去
while( true )
do
print("循环将永远执行下去")
break
end
--7、Lua 流程控制
--(1)、if语句
--[[
if(布尔表达式)
then
--[ 在布尔表达式为 true 时执行的语句 --]
end
Lua认为false和nil为假,true 和非nil为真。要注意的是Lua中 0 为 true
--]]
--定义变量
a = 10;
--使用 if 语句
if( a < 20 )
then
print("a 小于 20" ); --> a 小于 20
end
print("a 的值为:", a); --> a 的值为: 10
--(2)、if...else语句
--[[
第一种:
if(布尔表达式)
then
--[ 布尔表达式为 true 时执行该语句块 --]
else
--[ 布尔表达式为 false 时执行该语句块 --]
end
第二种:
if( 布尔表达式 1)
then
--[ 在布尔表达式 1 为 true 时执行该语句块 --]
elseif( 布尔表达式 2)
then
--[ 在布尔表达式 2 为 true 时执行该语句块 --]
else
--[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end
--]]
--(3)、if嵌套语句
--[[
if( 布尔表达式 1)
then
--[ 布尔表达式 1 为 true 时执行该语句块 --]
if(布尔表达式 2)
then
--[ 布尔表达式 2 为 true 时执行该语句块 --]
end
end
--]]
--8、Lua 函数
--[[
(1)、函数说明:
在Lua中,函数是对语句和表达式进行抽象的主要方法。
Lua 函数主要有两种用途:
1.完成指定的任务,这种情况下函数作为调用语句使用;
2.计算并返回值,这种情况下函数作为赋值语句的表达式使用。
--]]
--[[
(2)、函数定义:
optional_function_scope function function_name( arg1, arg2, arg3..., argn)
function_body
return result_params_comma_separated
end
解析:
optional_function_scope: 该参数是可选的,设置函数是全局函数还是局部函数,
默认为全局函数;若需设置函数为局部函数,需要使用关键字local。
function_name: 指定函数名称。
arg1, arg2, arg3..., argn:函数参数,多个参数以逗号隔开,函数也可以不带参数。
function_body:函数体,函数中需要执行的代码语句块。
result_params_comma_separated:函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。
--]]
--返回两个数中的最大值
function max(num1, num2)
if(num1 > num2) then
result = num1;
else
result = num2;
end
return result;
end
print("两值比较最大值为 ",max(10,4)) --> 两值比较最大值为 10
--将函数作为参数传递给函数
myprint = function(param)
print("这是打印函数 - ##",param,"##")
end
function add(num1,num2,funPrint)
result = num1 + num2
-- 调用传递的函数参数
funPrint(result)
end
myprint(10) -->这是打印函数 - ## 10 ##
-- myprint 函数作为参数传递
add(2,5,myprint) -->这是打印函数 - ## 7 ##
--(3)、多返回值
--[[
Lua函数可返回多个值,eg,string.find:返回匹配串"开始和结束的下标"
(如果不存在匹配串返回nil)。
--]]
s, e = string.find(" www.runoob.com", "runoob")
print(s, e) --> 6 11
--Lua函数中,在return后列出要返回的值列表,即可返回多值
--(4)、可变参数
--[[
可接受可变数目的参数:和C语言类似,在函数参数列表中使用三点(...)
表示函数有可变的参数。
Lua将函数的参数放在一个叫arg的表中,#arg 表示传入参数的个数
--]]
function average(...)
result = 0
local arg={...}
for i, v in ipairs(arg) do
result = result + v
end
print("总共传入 " .. #arg .. " 个数") --> 总共传入 6 个数
return result/#arg
end
print("平均值为",average(10,5,3,4,5,6)) --> 平均值为 5.5
--9、Lua 运算符
--[[
运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。
Lua提供了以下几种运算符类型:
算术运算符、关系运算符、逻辑运算符、其他运算符
--]]
--(1)、算术运算符:+(加法)、-(减法)、*(除法)、/(除法)、%(取余)、^(乘幂)、-(负号)
a = 21
b = 10
c = a + b
print("c 的值为 ", c ) --> c 的值为 31
--(2)、关系运算符:==、~=、>、<、>=、<=
a = 21
b = 10
if( a ~= b )
then
print("a不等于 b" )
else
print("a 等于 b" )
end --> a不等于 b
--(3)、逻辑运算符:and、or、not
a = true
b = true
if ( not( a and b) )
then
print("条件为 true" )
else
print("条件为 false" ) --> 条件为 false
end
--(4)、其他运算符:..(连接两个字符串)、#(一元运算符,返回字符串或表的长度)
--(5)、运算符优先级
--[[
从高到低:
^
not - (unary)
* /
+ -
..
< > <= >= ~= ==
and
or
除了^和..外所有的二元运算符都是左连接的。
--]]
--10、Lua字符串
--[[
(1)、字符串或串(String)是由数字、字母、下划线组成的一串字符。
Lua 语言中字符串可使用以下三种表示方式:
单引号间的一串字符。
双引号间的一串字符。
双中括号间的一串字符。
]]--
s1 = "Lua"
print("\"s1 是\"",s1) --> "s1 是" Lua
s2 = 'runoob.com'
print("s2 是",s2) --> s2 是 runoob.com
s3 = [["Lua 教程"]]
print("s3 是",s3) --> s3 是 "Lua 教程"
--[[
(2)、转义字符用于表示不能直接显示的字符,比如后退键,回车键,等。
如在字符串转换双引号可以使用 "\""。
\a:响铃(BEL)
\b:退格(BS) ,将当前位置移到前一列
\f:换页(FF),将当前位置移到下页开头
\n:换行(LF) ,将当前位置移到下一行开头
\r:回车(CR) ,将当前位置移到本行开头
\t:水平制表(HT) (跳到下一个TAB位置)
\v:垂直制表(VT)
\\:代表一个反斜线字符''\'
\':代表一个单引号(撇号)字符
\":代表一个双引号字符
\0:空字符(NULL)
\ddd:1到3位八进制数所代表的任意字符
\xhh:1到2位十六进制所代表的任意字符
--]]
--[[
(3)、字符串操作:Lua 提供了很多的方法来支持字符串的操作。
1)string.upper/lower(arg):字符串全部转为大/小写字母。
2)string.sub(s,i [,j]):取s中从i开始到j为止的自字符串。默认j为长度,-i表示倒数。
3)string.gsub(mainString,findString,replaceString[,num]):替换,
返回替换后的新字符串和替换次数。
mainString为要替换的字符串,findString 为被替换的字符,
replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换)
4)string.find (str, substr, [ init, [end] ]):在指定字符串中搜索指定内容,
返回其具体位置(第三个参数为索引)。不存在则返回 nil。
5)string.reverse(arg):字符串反转。
6)string.format(...):返回一个类似printf的格式化字符串。
7)string.char(arg):将整型数字转成字符并连接。
string.byte(arg[,int]):转换字符为整数值(可以指定某个字符,默认第一个字符)。
8)string.len(arg):计算字符串长度。
9)string.rep(string, n):返回字符串string的n个拷贝。
10)..:链接两个字符串。
11)string.gmatch(str, pattern):回一个迭代器函数,每一次调用这个函数,
返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。
如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。
12)string.match(str, pattern[, init]):只寻找源字串str中的第一个配对。
参数init可选, 指定搜寻过程的起点, 默认为1。
成功配对时, 函数将返回配对表达式中的所有捕获结果;
若未设置捕获标记, 则返回整个配对字符串。
当没有成功的配对时, 返回nil。
]]--
--[[
(4)、字符串格式化: string.format() 函数来生成具有特定格式的字符串,
函数的第一个参数是格式 , 之后是对应格式中每个代号的各种数据。
1)格式字符串可能包含以下的转义码:
%c - 接受一个数字, 并将其转化为ASCII码表中对应的字符
%d, %i - 接受一个数字,并将其转化为有符号的整数格式
%o - 接受一个数字,并将其转化为八进制数格式
%u - 接受一个数字,并将其转化为无符号整数格式
%x - 接受一个数字,并将其转化为十六进制数格式, 使用小写字母
%X - 接受一个数字,并将其转化为十六进制数格式, 使用大写字母
%e - 接受一个数字,并将其转化为科学记数法格式, 使用小写字母e
%E - 接受一个数字,并将其转化为科学记数法格式, 使用大写字母E
%f - 接受一个数字,并将其转化为浮点数格式
%g(%G) - 接受一个数字,并将其转化为%e(%E, 对应%G)及%f中较短的一种格式
%q - 接受一个字符串,并将其转化为可安全被Lua编译器读入的格式
%s - 接受一个字符串,并按照给定的参数格式化该字符串
2)进一步细化格式, 可以在%号后添加参数. 参数将以如下的顺序读入:
1.符号: 一个+号表示其后的数字转义符将让正数显示正号。
默认情况下只有负数显示符号.
2.占位符: 一个0, 在后面指定了字串宽度时占位用。不填时的默认占位符是空格。
3.对齐标识: 在指定了字串宽度时, 默认为右对齐, 增加-号可以改为左对齐。
4.宽度数值
5.小数位数/字串裁切: 在宽度数值后增加的小数部分n,
若后接f(%6.3f)则设定该浮点数的小数只保留n位,
若后接s(%5.3s)则设定该字符串只显示前n位.
--]]
--[[
(5)、匹配模式:Lua 中的匹配模式直接用常规的字符串来描述。
它用于模式匹配函数:
string.find, string.gmatch, string.gsub, string.match。
可在模式串中使用字符类:字符类指可以匹配一个特定字符集合内任何字符的模式项。
eg: '%d%d/%d%d/%d%d%d%d' 搜索 dd/mm/yyyy 格式的日期
下面的表列出了Lua支持的所有字符类:
单个字符(除 ^$()%.[]*+-? 外): 与该字符自身配对
.(点): 与任何字符配对
%a: 与任何字母配对
%c: 与任何控制符配对(例如\n)
%d: 与任何数字配对
%l: 与任何小写字母配对
%p: 与任何标点(punctuation)配对
%s: 与空白字符配对
%u: 与任何大写字母配对
%w: 与任何字母/数字配对
%x: 与任何十六进制数配对
%z: 与任何代表0的字符配对
%x(此处x是非字母非数字字符): 与字符x配对. 主要用来处理表达式中有功能的字符(^$()%.[]*+-?)的配对问题, 例如%%与%配对
[数个字符类]: 与任何[]中包含的字符类配对. 例如[%w_]与任何字母/数字, 或下划线符号(_)配对
[^数个字符类]: 与任何不包含在[]中的字符类配对. 例如[^%s]与任何非空白字符配对
当上述的字符类用“大写”书写时, 表示与非此字符类的任何字符配对。
Lua中的特殊字符如下:( ) . % + - * ? [ ^ $
·'%' 用作特殊字符的转义字符:
'%.' 匹配点; '%%' 匹配字符 '%'。
转义字符 '%'不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。
·在模式最前面加上符号 '^' 将锚定从字符串的开始处做匹配。
·在模式最后面加上符号 '$' 将使匹配过程锚定到字符串的结尾。
·如果'^'和'$'出现在其它位置,它们均没有特殊含义,只表示自身。
模式条目可以是:单个字符m
m:匹配该类别中任意单个字符;
m*:匹配零或多个该类的字符。这个条目总是匹配尽可能长的串;
m+:匹配一或更多个该类的字符。这个条目总是匹配尽可能长的串;
m-:匹配零或更多个该类的字符。这个条目总是匹配尽可能短的串;
m?:匹配零或一个该类的字符。 只要有可能,它会匹配一个;
%n:n可以从1到9;这个条目匹配一个等于n号捕获物(后面有描述)的子串。
%bxy:这里的x和y是两个明确的字符;这个条目匹配以x开始y结束,
且其中x和y保持平衡的字符串:即:
如果从左到右读这个字符串,对每次读到一个x就+1,读到一个y就 -1
最终结束处的那个 y 是第一个记数到 0 的 y。
eg:条目 %b() 可以匹配到括号平衡的表达式。
%f[set]:指边境模式;这个条目会匹配到一个位于 set 内某个字符之前的一个空串,
且这个位置的前一个字符不属于 set 。 集合 set 的含义如前面所述。
匹配出的那个空串之开始和结束点的计算就看成该处有个字符 '\0' 一样。
捕获:模式可以在内部用小括号括起一个子模式; 这些子模式被称为 捕获物。
当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。
捕获物以它们左括号的次序来编号。
eg:(a*(.)%w(%s*)):
·字符串中匹配到 "a*(.)%w(%s*)" 的部分保存在第一个捕获物中,编号 1 ;
·由 "." 匹配到的字符是 2 号捕获物;
·匹配到 "%s*" 的那部分是 3 号。
--]]
--11、Lua数组
--[[
数组:就是相同数据类型的元素按一定顺序排列的集合,可以是一维数组和多维数组。
Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。
--]]
--(1)、一维数组:其逻辑结构是线性表。可以用for循环出数组中的元素。
array = {"Lua", "Tutorial"}
for i= 0, 2 do
print(array[i])
end --> nil
--> Lua
--> Tutorial
-- 在 Lua 索引值是以 1 为起始,但你也可以指定 0 开始。
-- 也可以以负数为数组索引值:
arr = {}
for i= -2, 2 do
arr[i] = i *2
end
for i = -2,2 do
print(arr[i])
end --> -4 -2 0 2 4
print(arr[-1]) --> -2
--(2)、多维数组:数组中包含数组或一维数组的索引键对应一个数组
--一个三行三列的阵列多维数组
-- 初始化数组
arr = {}
for i=1,3 do
arr[i] = {}
for j=1,3 do
arr[i][j] = i * j
end
end
-- 访问数组
for i=1,3 do
for j=1,3 do
print(arr[i][j])
end
end
--12、Lua迭代器
--[[
迭代器:是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,
每个迭代器对象代表容器中的确定的地址。
在Lua中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。
标准库提供了几种迭代器,包括用于迭代文件中个的每行(io.lines)、
迭代table元素(pairs)、迭代数组元素的(ipairs)、迭代字符串中的单词
(string.gmatch),还可以编写自己的迭代器。
pairs 和 ipairs异同
同:都是能遍历集合(表、数组)
异:
·ipairs 仅仅遍历值,按照索引升序遍历,索引中断停止遍历。
固定地从key值1开始,下次key累加1进行遍历。(无key,默认数字索引)
即不能返回 nil,只能返回数字 0,如果遇到 nil 则退出。
它只能遍历到集合中出现的第一个不是整数的 key。
·pairs 能遍历集合的所有元素。即 pairs 可以遍历集合中所有的 key,
并且除了迭代器本身以及遍历表本身还可以返回 nil
--]]
--(1)、泛型 for 迭代器
--[[
泛型 for 在自己内部保存迭代函数,实际上它保存三个值:
迭代函数、状态常量、控制变量。
泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:
for k, v in pairs(t) do
print(k, v)
end
-- k, v为变量列表;pair(t)为表达式列表。
在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。
Lua 的迭代器包含以下两种类型:
无状态的迭代器、多状态的迭代器
--]]
--[[
(2)、无状态的迭代器:不保留任何状态的迭代器。
在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。
每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,
一个无状态的迭代器只利用这两个值可以获取下一个元素。
这种无状态迭代器的典型的简单的例子:ipairs,遍历数组的每一个元素。
--]]
function square(maxCount, curNum)
if curNum < maxCount
then
curNum = curNum + 1
return curNum, curNum * curNum
end
end
for i,n in square,3,0
do
print(i,n)
end --> 1 1
--> 2 4
--> 3 9
--[[
(3)、多状态的迭代器:很多情况下,迭代器需要保存多个状态信息,
而不是简单的状态常量和控制变量,最简单的方法是使用“闭包”,
还有一种方法就是将所有的状态信息封装到table内,
将table作为迭代器的状态常量,
因为这种情况下可以将所有的信息存放在table内,
所以迭代函数通常不需要第二个参数。
--]]
--创建了自己的迭代器
function elemIter (collection)
local index = 0
local count = #collection
-- 闭包函数
return function()
index = index + 1
if index <= count
then
-- 返回迭代器的当前元素
return collection[index]
end
end
end
array = {"B", "A", "R"}
for elem in elemIter(array)
do
print(elem)
end
--13、Lua table(表)
--[[
table 是 Lua 的一种数据结构,用来帮助创建不同的数据类型,如:数字、字典等。
使用关联型数组,可用任意类型的值来作数组的索引,但这个值不能是 nil。
不固定大小,根据所需进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。
例如string.format表示使用"format"来索引table string。
--]]
--(1)、table(表)的构造
-- 初始化表
mytable = {}
-- 指定值
mytable[1]= "Lua"
-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存
--[[
当有table a 并设置元素,将 a 赋值给 b,则 a 与 b 都指向同一个内存。
改变b的内容同样会改变a的;
但若a或b被赋值为b,另外一个table里面的值仍能访问。
--]]
-- 简单的 table,描述上述叙述
t1 = {}
print("t1的类型是 ",type(t1)) --> t1的类型是table
t1[1]= "A"
t1["2"] = "B"
print("t1的1 = ", t1[1]) --> t1的1 = A
print("t1的\"2\" = ", t1["2"]) --> t1的"2" = B
-- t2和t1的是指同一个 table
t2 = t1
print("t2的1 = ", t2[1]) --> t2的1 = A
print("t2的\"2\" = ", t2["2"]) --> t2的"2" = B
t2["2"] = "C"
print("t2改变后t1的\"2\" = ", t1["2"]) --> t2改变后t1的"2" = C
-- 释放变量
t2 = nil
print("t2 = ", t2) --> t2 = nil
-- t1 仍然可以访问
print("\"2\" = ", t1["2"]) -->"2" = C
t1 = nil
print("t1 = ", t1) --> t1 = nil
--(2)、Table 操作
--[[
Table操作常用的方法:
·table.concat (table [, sep [, start [, end] ] ]):
列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。
·table.insert (table, [pos,] value):在table的数组部分指定位置(pos)插入值为value的一个元素。
pos参数可选, 默认为数组部分末尾.
·table.maxn (table):指定table中所有正数key值中最大的key值。
如果不存在key值为正数的元素, 则返回0。(Lua5.2之后该方法不存在)
·table.remove (table [, pos]):返回table数组部分位于pos位置的元素。
其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。
·table.sort (table [, comp]):对给定的table进行升序排序。
--]]
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
tbl = {[1] = "a", [2] = "b", [3] = "c", [26] = "z"}
--26和之前的数字不连续, 所以不算在数组部分内
print("tbl 长度 ", #tbl) --> tbl 长度 3
print("tbl 最大值 ", table_maxn(tbl)) --> tbl 最大值 26
--14、Lua 模块与包
--[[
模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,
可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,
有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table:创建一个模块很简单,
就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table。
--]]
--(1)、创建自定义模块 module.lua,文件代码格式如下:
-- 文件名为 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
--模块的结构:即table 的结构,So可以像操作调用table里的元素那样来操作调用模块里的常量或函数。
--(2)、require 函数
--[[
require 函数:用来加载模块,只需调用即可:
require("<模块名>") 或 require "<模块名>"
·执行 require 后会返回一个由模块常量或函数组成的 table,
并且还会定义一个包含该 table 的全局变量。
-- test_module.lua 文件
-- module 模块为上文提到到 module.lua
require("module")
print(module.constant) --> 这是一个常量
module.func3() --> 这是一个私有函数!
·给加载的模块定义一个别名变量,方便调用:
-- test_module2.lua 文件
-- module 模块为上文提到到 module.lua
-- 别名变量 m
local m = require("module")
print(m.constant) --> 这是一个常量
m.func3() --> 这是一个私有函数!
--]]
--(3)、加载机制
--[[
对于自定义的模块,模块文件不是放在哪个文件目录都行,
函数 require 有它自己的文件路径加载策略,它会尝试
从 Lua 文件或 C 程序库中加载模块。
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,
当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。
如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下
打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),
例如把 "~/lua/" 路径加入 LUA_PATH 环境变量里:
#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"
文件路径以 ";" 号分隔,最后的 2 个 ";;" 表示新加的路径后面加上原来的默认路径。
更新环境变量参数,使之立即生效: source ~/.profile
--]]
--(4)、C包
--[[
Lua和C是很容易结合的,使用C为Lua写包。
与Lua中写包不同,C包在使用以前必须首先加载并连接,
在大多数系统中最容易的实现方式是通过动态连接库机制。
Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。
这个函数有两个参数:库的绝对路径和初始化函数。所以典型的调用的例子如下:
local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")
如果加载动态库或者查找初始化函数时出错,loadlib将返回nil和错误信息。
我们可以修改前面一段代码,使其检测错误然后调用初始化函数:
local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下
local f = assert(loadlib(path, "luaopen_socket"))
f() -- 真正打开库
将stub文件所在的目录加入到LUA_PATH,这样设定后就可以使用require函数加载C库了。
--]]
--15、Lua 元表(Metatable)
--[[
在 Lua table 中可以访问对应的key来得到value值,
但是却无法对两个 table 进行操作。
Lua 提供了元表(Metatable),允许改变table的行为,每个行为关联了对应的元方法。
当Lua试图对两个表进行相加时,先检查两者之一是否有元表,
之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。
"__add"等即时字段,其对应的值(往往是一个函数或是table)就是"元方法"。
有两个很重要的函数来处理元表:
setmetatable(table,metatable): 对指定table设置元表(metatable),
如果元表(metatable)中存在__metatable键值,setmetatable会失败 。
getmetatable(table): 返回对象的元表(metatable)。
--]]
--(1)、如何对指定的表设置元表,如何返回对象元表
--[[
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
--上面代码等价于mytable = setmetatable({},{})
getmetatable(mytable) -- 返回mymetatable
--]]
--(2)、__index 元方法:metatable 最常用的键。
--[[
当通过键来访问table时,如果这个键没有值,
那Lua就会寻找该table的metatable(假定有metatable)
中的__index键。如果__index包含一个表格,Lua会在表格中查找相应的键。
若__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
__index 元方法查看表中元素是否存在:
若不存在,返回结果为 nil;如果存在则由 __index 返回结果。
mytable = setmetatable({key1 = "value1"}, {
__index = function(mytable, key)
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
})
print(mytable.key1,mytable.key2) --> value1 metatablevalue
实例解析:
·mytable 表赋值为 {key1 = "value1"}。
·mytable 设置了元表,元方法为 __index。
·在mytable表中查找 key1,如果找到,返回该元素,找不到则继续。
·在mytable表中查找 key2,如果找到,返回 metatablevalue,找不到则继续。
·判断元表有没有__index方法,如果__index方法是一个函数,则调用该函数。
·元方法中查看是否传入 "key2" 键的参数(mytable.key2已设置),
如果传入 "key2" 参数返回 "metatablevalue",否则返回 mytable 对应的键值。
上面代码等价于:
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)
--]]
--[[
Lua查找一个表元素时的规则,其实就是如下3个步骤:
1.在表中查找,如果找到,返回该元素,找不到则继续
2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续。
3.判断元表有没有__index方法,如果__index方法为nil,则返回nil;
若__index方法是一个表,则重复1、2、3;
若__index方法是一个函数,则返回该函数的返回值。
--]]
--(3)、__newindex 元方法
--[[
__newindex 元方法用来对表更新,__index则用来对表访问。
当给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:
若存在,则调用这个函数而不进行赋值操作。
-- __newindex 元方法的应用
mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })
print(mytable.key1) --> value1
mytable.newkey = "新值2"
print(mytable.newkey,mymetatable.newkey) --> nil 新值2
mytable.key1 = "新值1"
print(mytable.key1,mymetatable.key1) --> 新值1 nil
表设置了__newindex,在对新索引键(newkey)赋值时(mytable.newkey = "新值2"),
会调用元方法,而不进行赋值。而如果对已存在的索引键(key1),
则会进行赋值,而不调用元方法 __newindex。
--]]
--[[
使用了 rawset 函数来更新表:
--单独的一个文件运行
mytable = setmetatable({key1 = "value1"}, {
__newindex = function(mytable, key, value)
rawset(mytable, key, "\""..value.."\"")
end
})
mytable.key1 = "new value"
mytable.key2 = 4
print(mytable.key1,mytable.key2) --> new value "4"
--]]
--(4)、为表添加操作符
--[[
--单独的一个文件运行
--自定义计算表中最大键值函数 table_maxn,即计算表的元素个数
function maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
--两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
__add = function(mytable, newtable)
for i = 1, maxn(newtable) do
table.insert(mytable, maxn(mytable)+1,newtable[i])
end
return mytable
end
})
secondtable = {4,5,6}
mytable = mytable + secondtable
for k,v in ipairs(mytable) do
print(k,v)
end
--]]
--其他操作:__sub、__mul、__div、__mod、__unm、__concat、__eq、__lt、__le
--(5)、__call 元方法:在 Lua 调用一个值时调用。
--(6)、__tostring 元方法:用于修改表的输出行为。
--16、Lua 协同程序(coroutine)
--(1)、什么是协同(coroutine)?
--[[
Lua 协同程序(coroutine)与线程比较类似:
拥有独立的堆栈,独立的局部变量,独立的指令指针,
同时又与其它协同程序共享全局变量和其它大部分东西。
协同是非常强大的功能,但是用起来也很复杂。
--]]
--(2)、线程和协同程序区别
--[[
·一个具有多个线程的程序可以同时运行几个线程
·协同程序却需要彼此协作的运行。
在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有
在明确的被要求挂起的时候才会被挂起。
协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。
--]]
--(3)、基本语法
--[[
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的线程号
--]]
--[[
co = coroutine.create(
function(i)
print(i);
end
)
coroutine.resume(co, 1) --> 1
print(coroutine.status(co)) --> dead
co = coroutine.wrap(
function(i)
print(i);
end
)
co(1) --> 1
co2 = coroutine.create(
function()
for i=1,10 do
print(i)
if i == 3 then
print(coroutine.status(co2)) --> running
print(coroutine.running()) --> thread: 0072EE60
end
coroutine.yield()
end
end
)
coroutine.resume(co2) --> 1
coroutine.resume(co2) --> 2
coroutine.resume(co2) --> 3
print(coroutine.status(co2)) --> suspended
print(coroutine.running()) --> null
--coroutine在底层实现就是一个线程。
--当create一个coroutine的时候就是在新线程中注册了一个事件。
--当使用resume触发事件的时候,create的coroutine函数就被执行了,
--当遇到yield的时候就代表挂起当前线程,等候再次resume触发事件。
--]]
--[[
--新的文件中:实例
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("--分割线----")
print("main", coroutine.resume(co, "r")) --> true 11 -9
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) --> true 10 end
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) --> cannot resume dead coroutine
print("---分割线---")
--]]
--[[
--结果:
第一次协同程序执行输出 1 10
foo 函数输出 2
main true 4
--分割线----
第二次协同程序执行输出 r
main true 11 -9
---分割线---
第三次协同程序执行输出 x y
main true 10 结束协同程序
---分割线---
main false cannot resume dead coroutine
---分割线---
--解释:
·调用resume,将协同程序唤醒,resume操作成功返回true,否则返回false;
·协同程序运行;
·运行到yield语句;
·yield挂起协同程序,第一次resume返回;(注意:此处yield返回,参数是resume的参数)
·第二次resume,再次唤醒协同程序;(注意:此处resume的参数中,除了第一个参数,剩下的参数将作为yield的参数)
·yield返回;
·协同程序继续运行;
·如果使用的协同程序继续运行完成后继续调用 resume方法则输出:cannot resume dead coroutine
resume和yield的配合强大之处在于:resume处于主程中,它将外部状态(数据)传入到协同程序内部;
而yield则将内部的状态(数据)返回到主程中。
--]]
--(4)、生产者-消费者问题
--[[
local newProductor
--生产者
function productor()
local i = 0
while true do
i = i + 1
--将生产的物品发送给消费者
send(i)
end
end
--消费者
function consumer()
while true do
-- 从生产者那里得到物品
local i = receive()
print(i)
end
end
--接收物品
function receive()
local status, value = coroutine.resume(newProductor)
return value
end
--发送物品
function send(x)
-- x表示需要发送的值,值返回以后,就挂起该协同程序
coroutine.yield(x)
end
-- 启动程序
newProductor = coroutine.create(productor)
consumer()
--]]
--17、Lua文件I/O
--[[
Lua I/O 库用于读取和处理文件,分为简单模式(和C一样)、完全模式。
简单模式:拥有一个当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作。
完全模式:使用外部的文件句柄来实现。它以一种面向对象的形式,
将所有的文件操作定义为文件句柄的方法
打开文件操作语句如下: file = io.open (filename [, mode])
r 以“只读”方式打开文件,该“文件必须存在”。
w 打开“只写”文件,若文件存在则文件长度清为0,即该文件“内容会消失”。若文件不存在则“建立该文件”。
a 以“附加的方式打开只写”文件。若文件不存在,则会建立该文件,如果文件存在,
写入的数据会被“加到文件尾”,即文件原先的“内容会被保留”。(EOF符保留)
r+ 以可“读写”方式打开文件,该“文件必须存在”。
w+ 打开“可读写”文件,若文件存在则文件长度清为零,即该文件“内容会消失”。若文件不存在则“建立该文件”。
a+ 与a类似,但此文件“可读可写”
b “二进制模式”,如果文件是二进制文件,可以加上b
+ 号表示对文件既可以读也可以写
--]]
--(1)、简单模式(simple model):使用标准的 I/O 或使用一个当前输入文件和一个当前输出文件。
--新建一个文件执行
--[[
-- 以只读方式打开文件
file = io.open("test.lua", "r")
-- 设置默认输入文件为 test.lua
io.input(file)
-- 输出文件第一行
print(io.read())
-- 关闭打开的文件
io.close(file)
-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")
-- 设置默认输出文件为 test.lua
io.output(file)
-- 在文件最后一行添加 Lua 注释
io.write("-- test.lua 文件末尾注释")
-- 关闭打开的文件
io.close(file)
--]]
--[[
IO中的方法:
1、io.read([可选参数]):
·"*n" 读取一个数字并返回它。例:file.read("*n")
·"*a" 从当前位置读取整个文件。例:file.read("*a")
·"*l"(默认)读取下一行,在文件尾 (EOF) 处返回 nil。例:file.read("*l")
·number 返回一个指定字符个数的字符串,或在 EOF 时返回 nil。例:file.read(5)
2、io.tmpfile():返回一个临时文件句柄,该文件以更新模式打开,程序结束时自动删除
3、io.type(file): 检测obj是否一个可用的文件句柄
4、io.flush(): 向文件写入缓冲中的所有数据
5、io.lines(optional file name): 返回一个迭代函数,每次调用将获得文件中的一行内容,
当到文件尾时,将返回nil,但不关闭文件
--]]
--(2)、完全模式(complete model):在同一时间处理多个文件,使用 file:function_name 代替 io.function_name 方法。
--[[
-- 以只读方式打开文件
file = io.open("test.lua", "r")
-- 输出文件第一行
print(file:read())
-- 关闭打开的文件
file:close()
-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")
-- 在文件最后一行添加 Lua 注释
file:write("--test")
-- 关闭打开的文件
file:close()
--]]
--[[
1、read 的参数与简单模式一致。
2、file:seek(optional whence, optional offset): 设置和获取当前文件位置,成功则返回最终的文件位置(按字节),失败则返回nil加错误信息。参数 whence 值可以是:
·"set": 从文件头开始
·"cur": 从当前位置开始[默认]
·"end": 从文件尾开始
·offset:默认为0
不带参数file:seek()则返回当前位置,file:seek("set")则定位到文件头,file:seek("end")则定位到文件尾并返回文件大小
3、file:flush(): 向文件写入缓冲中的所有数据
4、io.lines(optional file name): 打开指定的文件filename为读模式并返回一个迭代函数,每次调用将获得文件中的一行内容,
当到文件尾时,将返回nil,并自动关闭文件。
若不带参数时io.lines() <=> io.input():lines(); 读取默认输入设备的内容,但结束时不关闭文件,如
for line in io.lines("main.lua") do
print(line)
end
--]]
--[[
--定位到文件倒数第 25 个位置并使用 read 方法的 *a 参数,
--即从当前位置(倒数第 25 个位置)读取整个文件。
-- 以只读方式打开文件
file = io.open("test.lua", "r")
file:seek("end",-25)
print(file:read("*a"))
-- 关闭打开的文件
file:close()
--]]
--18、Lua错误处理
--[[
任何程序语言中,都需要错误处理。错误类型有:语法错误 和 运行错误
--]]
--(1)、语法错误:通常是由于对程序的组件(如运算符、表达式)使用不当引起的。
--(2)、运行错误:是程序可以正常执行,但是会输出报错信息。
--(3)、错误处理:使用两个函数:assert 和 error 来处理错误。
--[[
1)、assert
local function add(a,b)
assert(type(a) == "number", "a 不是一个数字")
assert(type(b) == "number", "b 不是一个数字")
return a+b
end
add(10)
--结果:
lua: testIO.lua:51: b 不是一个数字
stack traceback:
[C]: in function 'assert'
testIO.lua:51: in function 'add'
testIO.lua:54: in main chunk
[C]: ?
--]]
--[[
2)、error函数:error (message [, level])
功能:终止正在执行的函数,并返回message的内容作为错误信息(error函数永远都不会返回)
通常情况下,error会附加一些错误位置的信息到message头部。
·Level参数指示获得错误的位置:
·Level=1[默认]:为调用error位置(文件+行号)
·Level=2:指出哪个调用error的函数的函数
·Level=0:不添加错误位置信息
--]]
--(4)、pcall 和 xpcall、debug
--[[
Lua中处理错误,可使用函数 pcall(protected call) 来包装需要执行的代码。
pcall接收一个函数和要传递个后者的参数,并执行,执行结果:
有错误、无错误;返回值true或者或false, errorinfo。
语法格式如下:
if pcall(function_name, ….) then
-- 没有错误
else
-- 一些错误
end
--]]
--[[
--交互式编程模式下执行
> =pcall(function(i) print(i) end, 33)
33
true
> =pcall(function(i) print(i) error('error..') end, 33)
33
false stdin:1: error..
> function f() return false,2 end
> if f() then print '1' else print '0' end
0
--]]
--pcall以一种"保护模式"来调用第一个参数,因此pcall可以捕获函数执行中的任何错误。
--[[
通常在错误发生时,希望落得更多的调试信息,而不只是发生错误的位置。
但pcall返回时,它已经销毁了调用桟的部分内容。
Lua提供了xpcall函数,xpcall接收第二个参数——一个错误处理函数,
当错误发生时,Lua会在调用桟展看(unwind)前调用错误处理函数,
So可在这个函数中使用debug库来获取关于错误的额外信息了。
debug库提供了两个通用的错误处理函数:
debug.debug:提供一个Lua提示符,让用户来价差错误的原因
debug.traceback:根据调用桟来构建一个扩展的错误消息
--]]
--[[
function myfun ()
n = n/nil
end
function myerrorhandler( err )
print( "ERROR:", err )
end
status = xpcall( myfun, myerrorhandler )
print( status)
--结果:
ERROR: test.lua:2: attempt to perform arithmetic on global 'n' (a nil value)
false
--]]
--19、Lua 调试(Debug)
--[[
Lua 提供了 debug 库用于提供创建自定义调速器的功能。
Lua 中 debug 库包含以下函数:
0. sethook ([thread,] hook, mask [, count]):
1. debug():进入一个用户交互模式,运行用户输入的每个字符串。
使用简单的命令以及其它调试设置,用户可以检阅全局变量和局部变量,
改变变量的值,计算一些表达式,等等。
输入一行仅包含 cont 的字符串将结束这个函数, 这样调用者就可以继续向下运行。
2. getfenv(object): 返回对象的环境变量。
3. gethook(optional thread): 返回三个表示线程钩子设置的值:当前钩子函数,当前钩子掩码,当前钩子计数
4. getinfo ([thread,] f [, what]):返回关于一个函数信息的表。
可直接提供该函数,也可以用一个数字 f 表示该函数。
数字 f 表示运行在指定线程的调用栈对应层次上的函数:
0 层表示当前函数(getinfo 自身);
1 层表示调用 getinfo 的函数 (除非是尾调用,这种情况不计入栈);等等。
如果 f 是一个比活动函数数量还大的数字, getinfo 返回 nil。
5. debug.getlocal ([thread,] f, local):此函数返回在栈的 f 层处函数的索引为 local 的局部变量的名字和值。
这个函数不仅用于访问显式定义的局部变量,也包括形参、临时变量等。
6. getmetatable(value):把给定索引指向的值的元表压入堆栈。
如果索引无效,或是这个值没有元表,函数将返回 0 并且不会向栈上压任何东西。
7. getregistry():返回注册表表,这是一个预定义出来的表,可以用来保存任何C代码想保存的Lua值。
8. getupvalue (f, up)此函数返回函数 f 的第 up 个上值的名字和值。
如果该函数没有那个上值,返回 nil 。
以 '(' (开括号)打头的变量名表示没有名字的变量(去除了调试信息的代码块)。
10. 将一个函数作为钩子函数设入。
字符串 mask 以及数字 count 决定了钩子将在何时调用。
掩码是由下列字符组合成的字符串,每个字符有其含义:
'c': 每当 Lua 调用一个函数时,调用钩子;
'r': 每当 Lua 从一个函数内返回时,调用钩子;
'l': 每当 Lua 进入新的一行时,调用钩子。
11. setlocal ([thread,] level, local, value):将value赋给栈上第level层函数的第local个局部变量。
如果没有那个变量,函数返回 nil 。 如果 level 越界,抛出一个错误。
12. setmetatable (value, table):将 value 的元表设为 table (可以是 nil)。 返回 value。
13. setupvalue (f, up, value):这个函数将 value 设为函数 f 的第 up 个上值。
如果函数没有那个上值,返回 nil 否则,返回该上值的名字。
14. traceback ([thread,] [message [, level] ]):如果 message 有,且不是字符串或 nil,
函数不做任何处理直接返回 message。 否则,它返回调用栈的栈回溯信息。
字符串可选项 message 被添加在栈回溯信息的开头。
数字可选项 level 指明从栈的哪一层开始回溯 (默认为 1 ,即调用 traceback 的那里)。
--]]
--(1)、调试类型
--[[
命令行调试器:RemDebug、clidebugger、ctrace、xdbLua、LuaInterface - Debugger、Rldb、ModDebug。
图形界调试器:SciTE、Decoda、ZeroBrane Studio、akdebugger、luaedit。
--]]
--20、Lua 垃圾回收
--[[
Lua 采用了自动内存管理,意味着:
无需操心新创建的对象需要的内存如何分配出来
无需考虑在对象不再被使用后怎样释放它们所占用的内存。
Lua 运行了一个“垃圾收集器”来收集all“死对象”(即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。
Lua 中所有用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。
Lua 实现了一个“增量标记-扫描收集器”。它使用“垃圾收集器间歇率”和"垃圾收集器步进倍率"来控制垃圾收集循环,
这两个数字都使用百分数为单位 (例如:值 100 在内部表示 1 )。
·垃圾收集器间歇率:控制着收集器需要在“开启新的循环前要等待多久”。增大这个值会减少收集器的积极性。
·当这个值<100时,收集器在开启新的循环前不会有等待。
·当设置这个值为200时,就会让收集器等到总内存使用量达到“之前的两倍”时才开始新的循环。
·垃圾收集器步进倍率:控制着收集器运作速度相对于内存分配速度的倍率。
增大这个值不仅会让收集器更加积极,还会增加每个增量步骤的长度。
·不要把这个值设得小于100,那样的话收集器就工作的太慢了以至于永远都干不完一个循环。
·默认值是200,这表示收集器以内存分配的“两倍”速工作。
若把步进倍率设为一个非常大的数字(比你的程序可能用到的字节数还大 10% ),
收集器的行为就像一个 stop-the-world 收集器。
接着你若把间歇率设为200,收集器的行为就和过去的 Lua 版本一样了:每次Lua使用的内存翻倍时,就做一次完整的收集。
--]]
--(1)、垃圾回收器函数
--[[
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"): 停止垃圾收集器的运行。在调用重启前,收集器只会因显式的调用运行。
--]]
--[[
tt = {"apple", "orange", "banana"}
print(collectgarbage("count"))
tt = nil
print(collectgarbage("count"))
print(collectgarbage("collect"))
print(collectgarbage("count"))
--结果:
24.412109375
24.4404296875
0
21.5126953125
--]]
--22、Lua 面向对象
--[[
面向对象编程(Object Oriented Programming,OOP)是一种非常流行的计算机编程架构。
以下几种编程语言都支持面向对象编程:
C++、Java、Objective-C、Smalltalk、C#、Ruby
--]]
--(1)、面向对象特征
--[[
1)封装:指能够把一个实体的信息、功能、响应都装入一个单独的对象中的特性。
2)继承:继承的方法允许在不改动原程序的基础上对其进行扩充,这样使得原功能得以保存,
而新功能也得以扩展。这有利于减少重复编码,提高软件的开发效率。
3)多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
4)抽象:抽象(Abstraction)是简化复杂的现实问题的途径,它可以为具体问题
找到最恰当的类定义,并且可以在最恰当的继承级别解释问题。
--]]
--(2)、Lua 中面向对象
--[[
对象由属性和方法组成。LUA中最基本的结构是table,用“table来描述对象的属性”。
用“function表示方法”。那么LUA中的类可以通过“table + function”模拟出来。
继承,可以通过“metetable”模拟出来(不推荐用,只模拟最基本的对象大部分时间够用了)。
Lua中的“表”不仅在某种意义上是一种对象。像对象一样,表也有状态(成员变量);
也有与对象的值独立的本性,特别是拥有两个不同值的对象(table)代表两个不同的对象;
一个对象在不同的时候也可以有不同的值,但它始终是一个对象;
与对象类似,表的生命周期与其由什么创建、在哪创建没有关系。对象有他们的成员函数,表也有:
--创建新函数
Account = {balance = 0}
function Account.withdraw (v)
Account.balance = Account.balance - v
end
--调用函数
Account.withdraw(100.00)
--]]
--[[
创建对象:是位类的实例分配内存的过程。每个类都有属于自己的内存并共享公共数据。
访问属性:使用点号“.”来访问类的属性。
访问成员函数:使用冒号“:”来访问类的成员函数。
--]]
--[[
-- Meta class
Rectangle = {area = 0, length = 0, breadth = 0}
-- 派生类的方法 new
function Rectangle:new (o,length,breadth)
o = o or {}
setmetatable(o, self)
self.__index = self
self.length = length or 0
self.breadth = breadth or 0
self.area = length*breadth;
return o
end
-- 派生类的方法 printArea
function Rectangle:printArea ()
print("矩形面积为 ",self.area)
end
-- 创建对象
r = Rectangle:new(nil,10,20)
-- 访问属性
print(r.length) --> 10
-- 访问成员函数
r:printArea() --> 矩形面积为 200
--]]
--(3)、Lua 继承:继承指一个对象直接使用另一对象的属性和方法。可用于扩展基础类的属性和方法。
--[[
-- Meta class
Shape = {area = 0}
-- 基础类方法 new
function Shape:new(o, side)
o = o or {}
setmetatable(o, self)
self.__index = self
side = side or 0
self.area = side*side
return o
end
--基础类方法 printArea
function Shape:printArea()
print("面积为 ",self.area)
end
-- 创建对象
myshape = Shape:new(nil, 10)
myshape:printArea() --> 面积为 100
--1、正方形
Square = Shape:new()
-- 派生类方法 new
function Square:new (o,side)
o = o or Shape:new(o,side)
setmetatable(o, self)
self.__index = self
return o
end
-- 派生类方法 printArea
function Square:printArea ()
print("正方形面积为 ",self.area)
end
-- 创建对象
mysquare = Square:new(nil,10)
mysquare:printArea() -->正方形面积为 100
-- 2、矩形
Rectangle = Shape:new()
-- 派生类方法 new
function Rectangle:new (o,length,breadth)
o = o or Shape:new(o)
setmetatable(o, self)
self.__index = self
self.area = length * breadth
return o
end
-- 派生类方法 printArea
function Rectangle:printArea ()
print("矩形面积为 ",self.area)
end
-- 创建对象
myrectangle = Rectangle:new(nil,10,20)
myrectangle:printArea() -->矩形面积为 200
--]]
--(4)、函数重写:重写基础类的函数,在派生类中定义自己的实现方式
--23、Lua 数据库访问(略)