Lua入门总结

参考资料:菜鸟教程——Lua教程

--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 = [[
<html>
<head></head>
<body>
    <a href="http://www.runoob.com/">菜鸟教程</a>
</body>
</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 数据库访问(略)

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值