Lua学习笔记

Lua 笔记

菜鸟教程-Lua教程

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

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

基本语法

注释

-- 单行注释
--[[
多行注释
--]]

分号

语句结尾加不加分号都可以

重复定义

下面定义的变量会覆盖上面定义的

a = 1
print(a)     -- 1
local a = 2
print(a)     -- 2

Lua 数据类型

Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。

Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。

数据类型描述
nil这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。
boolean包含两个值:false和true。
number表示双精度类型的实浮点数
string字符串由一对双引号或单引号来表示
function由 C 或 Lua 编写的函数
userdata表示任意存储在变量中的C数据结构
thread表示执行的独立线路,用于执行协同程序
tableLua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。

Lua 变量

变量在使用前,需要在代码中进行声明,即创建该变量。

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

Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。

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

变量的默认值均为 nil。

访问局部变量的速度比全局变量更快。

-- 变量范围
a = 5          -- 全局变量
local b = 6    -- 局部变量

function joke()
    c = 7        -- 全局变量
    local d = 8  -- 局部变量
    print("----------------------")
    print(b)
end

joke()        -- 6
print(c,d)    -- 7  nil
print(b)      -- 6

Lua 循环

Lua中没有提供continue,可以使用goto语句达到continue的效果

while

while 条件

do

​ do something...

end

a = 10
while( a < 20 )
do
   print("a 的值为:", a)
   a=a+1
   if( a > 15)
   then
      --[ 使用 break 语句终止循环 --]
      break
   end
end
--[[
运行结果:
a 的值为:    10
a 的值为:    11
a 的值为:    12
a 的值为:    13
a 的值为:    14
a 的值为:    15
--]]

for

数值for循环

Lua 编程语言中数值 for 循环语法格式:

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

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

实例

function f(x) 
  print("function") 
  return x*2 
end

for i=1,f(2) 
do
  print(i)
end
 
for i=10,1,-1 do
  print(i)
end

for的三个表达式在循环开始前一次性求值,以后不再进行求值。比如上面的f(x)只会在循环开始前执行一次,其结果用在后面的循环中。

验证如下:

实例

#!/usr/local/bin/lua  
function f(x) 
  print("function") 
  return x*2 
end
for i=1,f(5)
do
    print(i) 
end

以上实例输出结果为:

function
1
2
3
4
5
6
7
8
9
10

可以看到 函数f(x)只在循环开始前执行一次。

goto实现continue

for i=1, 5 
do
    if i <= 2 then
        print(i, "yes continue")
        goto continue
    end
    print(i, " no continue")
    goto over
    ::continue::
    -- print([[i'm end]])
end

::over::
    print([[i'm over]])

--[[
运行结果:
1    yes continue
2    yes continue
3     no continue
i'm over
--]]

repeat until

和while循环差不多,until条件为true是停止循环

a = 10
repeat
    print("a = " .. a)
    a = a + 1
until(a > 15)

Lua 流程控制语句

--[ 定义变量 --]
a = 100;
b = 200;

--[ 检查条件 --]
if( a == 100 )
then
   --[ if 条件为 true 时执行以下 if 条件判断 --]
   if( b == 200 )
   then
      --[ if 条件为 true 时执行该语句块 --]
      print("a 的值为 100 b 的值为 200" );
   end
end
print("a 的值为 :", a );
print("b 的值为 :", b );

ipairs和pairs的区别

pairs会迭代每一项,忽略value为nil的项

ipairs从key为1开始迭代,每次key+1迭代,遇到value为nil停止迭代,遇到不是key+1规律停止迭代

local tabFiles = {
[1] = "test1",
[2] = "test2",
[3] = nil,
[6] = "test6",
[4] = "test4"
}

for k,v in pairs(tabFiles)
do
    print(k,v)

end

print("----------")

for k,v in ipairs(tabFiles)
do
    print(k,v)

end

--[[
6    test6
2    test2
1    test1
4    test4
----------
1    test1
2    test2
--]]

Lua 函数

-- 查出最大值和相应的key

function max(arr)
    local maxKey = 1
    local maxValue = arr[maxKey]
    for k,v in ipairs(arr)
    do
        if(v > maxValue) then
            maxValue = v
            maxKey = k

        end

    end
    return maxKey,maxValue
end


local arr = {2,4,6,8,10}
local a,b = max(arr)

print(a,b)
--[[
5    10
--]]

Lua 各数据类型传递方式

赋值函数参数
String
Number
Function
Table引用
-- 传递方式

-- number类型
a = 1

b = a

b = b + 1

print(a)  -- 1
print(b)  -- 2


print("----------------------------")

-- string类型
str1 = "Hello"
str2 = str1
str2 = str2 .. " world"
print(str1)  -- Hello
print(str2)  -- Hello world


print("----------------------------")
-- Table类型
arr1 = {1,2,3}
arr2 = arr1
table.insert(arr2,4)
for k,v in ipairs(arr1)
do
    print(k,v)

end
--[[
1    1
2    2
3    3
4    4
--]]

print("---------------")

for k,v in ipairs(arr2)
do
    print(k,v)

end
--[[
1    1
2    2
3    3
4    4
--]]
print("----------------------------")

-- 函数类型
fun1 = function ()
    print("fun1")
end

fun2 = fun1

fun2 = function ()
    print("fun2")
end

fun1()  -- fun1
fun2()  -- fun2

print("----------------------------")

-- 在函数中传递数字、字符串、table、fun

function chaundi(str, num, tab, fun)
    str = "str888"
    num = 888
    tab = {8, 9, 10}

    fun = function()
        print("888")
    end


end

chuanStr = "str"
chuanNum = 1
chuanTab = {1, 2, 3}
chuanFun = function ()
    print("chuanFun")
end

chaundi(chuanStr, chuanNum, chuanTab, chuanFun)

print(chuanStr)  -- str
print(chuanNum)  -- 1

for k,v in pairs(chuanTab)
do
    print(k, v)

end
--[[
1    1
2    2
3    3
--]]
chuanFun() -- chuanFun

Lua select函数和可变参数

select("#",...)

返回table的长度,和#{...}功能一样

select(i,...)

把结果返回出来和直接print的效果过不一样

把结果返回出来:返回的是key为i的value

直接print:返回的是从指定key位置开始往后所有的项

function average(className, ...)
    local sum = 0
    -- local ageArr = {...}

    for k,v in ipairs{...}  -- 还可以写成 for k,v in ipairs(ageArr)
    do
        sum = sum + v
        local age = select(k,...)
        print(age)                 -- 8 9 10
        print(select(k,...))       -- 8 9 10、9 10、10

    end

    print(className .. "的平均年龄为" .. sum/select("#",...) .. "岁") -- 三年级一班的平均年龄为9岁

end

average("三年级一班",8,9,10)

Lua 运算符优先级

^
not    - (unary)
*      /
+      -
..
<      >      <=     >=     ~=     ==
and
or

除了 ^.. 外所有的二元运算符都是左连接的。

a+i < b/2+1          <-->       (a+i) < ((b/2)+1)
5+x^2*8              <-->       5+((x^2)*8)
a < y and y <= z     <-->       (a < y) and (y <= z)
-x^2                 <-->       -(x^2)
x^y^z                <-->       x^(y^z)

实例

我们可以通过以下实例来更加透彻的了解 Lua 语言运算符的优先级:

a = 20
b = 10
c = 15
d = 5

e = (a + b) * c / d;-- ( 30 * 15 ) / 5
print("(a + b) * c / d 运算值为  :",e )

e = ((a + b) * c) / d; -- (30 * 15 ) / 5
print("((a + b) * c) / d 运算值为 :",e )

e = (a + b) * (c / d);-- (30) * (15/5)
print("(a + b) * (c / d) 运算值为 :",e )

e = a + (b * c) / d;  -- 20 + (150/5)
print("a + (b * c) / d 运算值为   :",e )

以上程序执行结果为:

(a + b) * c / d 运算值为  :    90.0
((a + b) * c) / d 运算值为 :    90.0
(a + b) * (c / d) 运算值为 :    90.0
a + (b * c) / d 运算值为   :    50.0

Lua 字符串

https://www.runoob.com/lua/lua-strings.html

Lua 数组

二维数组

-- 二维数组
arr = {}
for i = 1, 3
do
    arr[i] = {}
    for j = 1, 2
    do
        arr[i][j] = i * j
    end

end


for k,v in pairs(arr)
do
    for key,value in pairs(v)
    do
        print("(" .. k .. "," .. key .. ") = " .. value)
    end
    print("----")
end
--[[
(1,1) = 1
(1,2) = 2
----
(2,1) = 2
(2,2) = 4
----
(3,1) = 3
(3,2) = 6
----
--]]

Lua 迭代器

迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。

在 Lua 中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。

泛型 for 迭代器

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

状态常量每次传给迭代函数的值不会变化,控制变量会把上次迭代后控制变量的值赋值给迭代函数,一般控制变量每次都不一样

function pf(cl,bl)
    if cl > bl then
        bl = bl + 1
        return bl,bl^2
    end
end

for k,v in pf,5,0
do
    print(k,v)
end
--[[
1    1
2    4
3    9
4    16
5    25
--]]

图解

image-20200804185922287

手写实现一个ipairs迭代器

function iter (a, i)
    i = i + 1
    local v = a[i]
    if v then
       return i, v
    end
end

function ipairs (a)
    return iter, a, 0
end

arr = {"Java","Python",nil,"Js"}
for k,v in ipairs(arr)
do
    print(k, v)
end
--[[
1    Java
2    Python
--]]

多状态迭代器


-- 手写实现ipairs迭代器  多状态迭代器
-- 闭包
function diypairs(arr)
    local index = 0
    local count = #arr
    return function ()
        index = index + 1
        if (index <= count) and (arr[index] ~= nil) then
            return index,arr[index]
        end
    end
end


arr = {"Java","Python",nil,"Js"}
for k,v in diypairs(arr)
do
    print(k, v)
end

--[[
1    Java
2    Python
--]]

Lua 闭包

function test()
    local i=0
    return function()
        i = i + 1
        return i
    end
end
c1=test()
c2=test()

---[[
print(c1())  -- 1
print(c1())  -- 2
print(c2())  -- 1
print(c2())  -- 2
--]]

Lua Table操作

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

接下来我们来看下这几个方法的实例。

Table 连接

我们可以使用 concat() 输出一个列表中元素连接成的字符串:

实例

fruits = {"banana","orange","apple"}
-- 返回 table 连接后的字符串
print("连接后的字符串 ",table.concat(fruits))

-- 指定连接字符
print("连接后的字符串 ",table.concat(fruits,", "))

-- 指定索引来连接 table
print("连接后的字符串 ",table.concat(fruits,", ", 2,3))

执行以上代码输出结果为:

连接后的字符串     bananaorangeapple
连接后的字符串     banana, orange, apple
连接后的字符串     orange, apple

插入和移除

以下实例演示了 table 的插入和移除操作:

实例

fruits = {"banana","orange","apple"}

-- 在末尾插入
table.insert(fruits,"mango")
print("索引为 4 的元素为 ",fruits[4])  -- 索引为 4 的元素为     mango

-- 在索引为 2 的键处插入
table.insert(fruits,2,"grapes")
print("索引为 2 的元素为 ",fruits[2])  -- 索引为 2 的元素为     grapes


-- 移出最后一个元素
print("最后一个元素为 ",fruits[5])  -- 最后一个元素为     mango
table.remove(fruits)
print("移除后最后一个元素为 ",fruits[5])  -- 移除后最后一个元素为     nil

-- 根据key移出元素
table.remove(fruits,2)
print(table.concat(fruits,", "))  -- banana, apple

Table 排序

默认排序规则是升序,对字母和数字有效字母按照ASCII码,

以下实例演示了 sort() 方法的使用,用于对 Table 进行排序:

实例

fruits = {"banana","orange","apple","grapes"}
print("排序前")
for k,v in ipairs(fruits) do
    print(k,v)
end

table.sort(fruits)
print("排序后")
for k,v in ipairs(fruits) do
    print(k,v)
end

执行以上代码输出结果为:

排序前
1    banana
2    orange
3    apple
4    grapes
排序后
1    apple
2    banana
3    grapes
4    orange

Table 最大值

table.maxn 在 Lua5.2 之后该方法已经不存在了,我们定义了 table_maxn 方法来实现。

以下实例演示了如何获取 table 中的最大值:

实例

function table_maxn(t)
 local mn=nil;
 for k, v in pairs(t) do
  if(mn==nil) then
   mn=v
  end
  if mn < v then
   mn = v
  end
 end
 return mn
end
tbl = {[1] = 2, [2] = 6, [3] = 34, [26] =5}
print("tbl 最大值:", table_maxn(tbl))
print("tbl 长度 ", #tbl)

执行以上代码输出结果为:

tbl 最大值:    34
tbl 长度     3

注意:

当我们获取 table 的长度的时候无论是使用 # 还是 table.getn 其都会在索引中断的地方停止计数,而导致无法正确取得 table 的长度。

可以使用以下方法来代替:

function table_leng(t)
  local leng=0
  for k, v in pairs(t) do
    leng=leng+1
  end
  return leng;
end

终极大招

获取最大值和相应的key和长度

function getMaxKVS(arr)
    local maxKey = 1
    local maxValue = arr[maxKey]
    local size = 0

    for k, v in pairs(arr)
    do
        size = size + 1
        if v > maxValue then
            maxKey = k
            maxValue = v
        end
    end

    return maxKey, maxValue, size
end

arr = {
[1] = 1,
[2] = 2,
[5] = 5,
[8] = 50,
[10] = 20
}
k,v, s = getMaxKVS(arr)
print("最大值为:"..v..",响应的key为:"..k..",长度为:"..s) -- 最大值为:50,响应的key为:8,长度为:5

Lua 元表

在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。

因此 Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。

例如,使用元表我们可以定义Lua如何计算两个table的相加操作a+b。

当Lua试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add"等即时字段,其对应的值(往往是一个函数或是table)就是"元方法"。

有两个很重要的函数来处理元表:

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

以下实例演示了如何对指定的表设置元表:

mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表

以上代码也可以直接写成一行:

mytable = setmetatable({},{})

以下为返回对象元表:

getmetatable(mytable)                 -- 这回返回mymetatable

__index 元方法

这是 metatable 最常用的键。

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

实例一

arr1 = {1,2,3}
mytable = setmetatable(arr1,{
    __index = function(mytable, key)
        print("key:" .. key .. "不存在")
        return -1
    end
})

print(mytable[5])
--[[
key:5不存在
-1
--]]

实例二

arr1 = {1,2,3}
arr2 = {4,5,6,7,8}
mytable = setmetatable(arr1,{
    __index = arr2
})

print(mytable[5]) -- 8

实例解析

  • mytable 表赋值为 {key1 = "value1"}
  • mytable 设置了元表,元方法为 __index。
  • 在mytable表中查找 key1,如果找到,返回该元素,找不到则继续。
  • 在mytable表中查找 key2,如果找到,返回 metatablevalue,找不到则继续。
  • 判断元表有没有__index方法,如果__index方法是一个函数,则调用该函数。
  • 元方法中查看是否传入 "key2" 键的参数(mytable.key2已设置),如果传入 "key2" 参数返回 "metatablevalue",否则返回 mytable 对应的键值。

__newindex 元方法

__newindex 元方法用来对表更新,__index则用来对表访问 。

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

实例一

mytable = setmetatable({key1 = "value1"}, {
  __newindex = function(mytable, key, value)
        print("新增了元素")
        rawset(mytable, key, value)

  end
})

mytable.key1 = "new value"
mytable.key2 = "4"

print(mytable.key1, mytable.key2)
--[[
新增了元素
new value    4
--]]

实例二

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

实例解析

只有在元表新增元素时才会触发

如果__index为函数则触发函数

如果__index为一个Table则是对指定Table进行新增,元表不会有任何变化

__add 元方法

__add 键包含在元表中,并进行相加操作。 表中对应的操作列表如下:(注意:__是两个下划线)

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

__add 元表加操作的方法,函数第一个参数为加数,第二个参数为被加数

实例一

-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
  __add = function(mytable, newtable)
        print("------mytable------")
        for k,v in pairs(mytable)
        do
            print(k, v)
        end

        print("------newtable------")
        for k,v in pairs(newtable)
        do
            print(k, v)
        end

        return table.concat(mytable,",") .. "," .. table.concat(newtable,",")

  end
})

secondtable = {4,5,6}

tableRes = mytable + secondtable
print("------tableRes------")
print(tableRes)
--[[
1    1
2    2
3    3
------newtable------
1    4
2    5
3    6
------tableRes------
1,2,3,4,5,6
--]]

实例二

-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
  __add = function(mytable, newtable)

        for k,v in pairs(newtable)
        do
            table.insert(mytable,v)
        end
        return mytable
  end
})

secondtable = {4,5,6}

tableRes = mytable + secondtable
print("------tableRes------")
for k, v in pairs(tableRes)
do
    print(k, v)
end

--[[
------tableRes------
1    1
2    2
3    3
4    4
5    5
6    6
--]]

__call 元方法

把Table当作一个函数使用

第一个参数是固定的为元表,剩下的参数可以是任意个

实例

mytable = setmetatable({1,2,3},{
    __call = function(mytable, params)
        print(params)
        return {4,5,6}
    end
})

res = mytable("hello")
print(table.concat(res,","))
--[[
hello
4,5,6
--]]

__tostring 元方法

类似java的tostring,修改表的输出行为

实例

mytable = setmetatable({1, 2, 3},{
    __tostring = function(mytable)
        return table.concat(mytable, ",")
    end

})

print(mytable) -- 1,2,3

Lua 模块与包

模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。

Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。以下为创建自定义模块 module.lua,文件代码格式如下:

-- 文件名 module.lua

module = {}

-- 定义一个常量
module.constant = "这是一个常量"

-- 定义一个函数
module.fun1 = function()
    print("hello fun1")
end

-- 定义一个私有的hanshu
local fun2 = function()
    print("这是一个私有的函数fun2")
end

module.fun3 = function()
    fun2()
end

return module

require 函数

Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用就可以了。例如:

require("<模块名>")

或者

require "<模块名>"

实例

require("module")
print(module.constant)  -- 这是一个常量
-- 上下两种写法都可以
md = require("module")
print(md.constant)  -- 这是一个常量

实例

md = require("module")
print(md.constant)
md.fun1()
md.fun3()
--[[
这是一个常量
hello fun1
这是一个私有的函数fun2
--]]

Lua 协同程序(coroutine)

什么是协同(coroutine)?

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

协同是非常强大的功能,但是用起来也很复杂。

线程和协同程序区别

线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。

在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。

协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。

基本语法

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

创建协同程序和查看状态


co = coroutine.create(
    function(i)
        print(coroutine.status(co))
        print(i);
    end
)

coroutine.resume(co, 1)
print(coroutine.status(co))

print("----------")


co = coroutine.wrap(
    function(i)
        print(i);
    end
)
co(2)
--[[
running
1
dead
----------
2
--]]

启动、暂停协同程序


co = coroutine.create(
    function(a, b)
        print(a + b)
        coroutine.yield()  -- 挂起coroutine
        print(a - b);
    end
)

coroutine.resume(co, 30, 20)

print("hello")

coroutine.resume(co)
--[[
50
hello
10
--]]

获取协同程序的返回值

第一个返回值为启动是否成功,第二个返回值为函数执行完成后的返回值

co = coroutine.create(
    function(a, b)
        print(a + b)
        -- coroutine.yield()  -- 挂起coroutine
        print(a - b);
        return a * b
    end
)

res1, res2 = coroutine.resume(co, 30, 20)
print(res1, res2)
--[[
50
10
true    600
--]]

协同程序挂起并return返回值


co = coroutine.create(
    function(a, b)
        print(a + b)
        coroutine.yield("挂起并返回值")  -- 挂起coroutine
        print(a - b);
        return a * b
    end
)

res1, res2 = coroutine.resume(co, 30, 20)
print(res1, res2)
res3, res4 = coroutine.resume(co)
print(res3, res4)
--[[
50
true    挂起并返回值
10
true    600
--]]

生产者消费者问题

-- 消费生产

local newproduct

-- 生产者
product = function()
    local i = 0
    while true
    do
        i = i + 1
        send(i)

    end

end

send = function(i)
    coroutine.yield(i) -- 返回并挂起
end

-- 消费者

consumer = function()
    while true
    do

        local i = receive()
        print(i)

    end
end

receive = function()

    local res1, res2 = coroutine.resume(newproduct)
    return res2
end


newproduct = coroutine.create(product)

-- consumer()

for i = 1,10
do

local res1, res2 = coroutine.resume(newproduct)
print(res2)

end

Lua 文件IO

Lua I/O 库用于读取和处理文件。分为简单模式(和C一样)、完全模式。

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

简单模式在做一些简单的文件操作时较为合适。但是在进行一些高级的文件操作的时候,简单模式就显得力不从心。例如同时读取多个文件这样的操作,使用完全模式则较为合适。

打开文件操作语句如下:

file = io.open (filename [, mode])

mode 的值有:

模式描述
r以只读方式打开文件,该文件必须存在。
w打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
a以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
r+以可读写方式打开文件,该文件必须存在。
w+打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a+与a类似,但此文件可读可写
b二进制模式,如果文件是二进制文件,可以加上b
+号表示对文件既可以读也可以写

简单模式

简单模式使用标准的 I/O 或使用一个当前输入文件和一个当前输出文件。

以下为 file.lua 文件代码,操作的文件为test.lua(如果没有你需要创建该文件),代码如下:

实例

-- 以只读方式打开文件
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)

执行以上代码,你会发现,输出了 test.lua 文件的第一行信息,并在该文件最后一行添加了 lua 的注释。如我这边输出的是:

-- test.lua 文件

在以上实例中我们使用了 io."x" 方法,其中 io.read() 中我们没有带参数,参数可以是下表中的一个:

模式描述
"*n"读取一个数字并返回它。例:file.read("*n")
"*a"从当前位置读取整个文件。例:file.read("*a")
"*l"(默认)读取下一行,在文件尾 (EOF) 处返回 nil。例:file.read("*l")
number返回一个指定字符个数的字符串,或在 EOF 时返回 nil。例:file.read(5)

其他的 io 方法有:

  • io.tmpfile():返回一个临时文件句柄,该文件以更新模式打开,程序结束时自动删除
  • io.type(file): 检测obj是否一个可用的文件句柄
  • io.flush(): 向文件写入缓冲中的所有数据
  • io.lines(optional file name): 返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回nil,但不关闭文件

完全模式

通常我们需要在同一时间处理多个文件。我们需要使用 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()

执行以上代码,你会发现,输出了 test.ua 文件的第一行信息,并在该文件最后一行添加了 lua 的注释。如我这边输出的是:

-- test.lua 文件

read 的参数与简单模式一致。

其他方法:

  • file:seek(optional whence, optional offset): 设置和获取当前文件位置,成功则返回最终的文件位置(按字节),失败则返回nil加错误信息。参数 whence 值可以是:

    • "set": 从文件头开始
    • "cur": 从当前位置开始[默认]
    • "end": 从文件尾开始
    • offset:默认为0

不带参数file:seek()则返回当前位置,file:seek("set")则定位到文件头,file:seek("end")则定位到文件尾并返回文件大小

  • file:flush(): 向文件写入缓冲中的所有数据
  • io.lines(optional file name): 打开指定的文件filename为读模式并返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回nil,并自动关闭文件。
    若不带参数时io.lines() <=> io.input():lines(); 读取默认输入设备的内容,但结束时不关闭文件,如:
for line in io.lines("main.lua") do

  print(line)

end

以下实例使用了 seek 方法,定位到文件倒数第 25 个位置并使用 read 方法的 *a 参数,即从当期位置(倒数第 25 个位置)读取整个文件。

实例

-- 以只读方式打开文件
file = io.open("test.lua", "r")

file:seek("end",-25)
print(file:read("*a"))

-- 关闭打开的文件
file:close()
</pre>
<p>我这边输出的结果是:</p>

st.lua 文件末尾--test

## Lua 面向对象

Lua 本身是不支持面向对象的,但是可以历用强大的table和函数实现面向对象

#### 手动实现面向对象
local Person = {
    name = "张三",
    age = 18
}
function Person:eat()
    print(self.name.."吃饭")
end


function Person:new()
    local t = {}
    setmetatable(t,{
        __index = self  -- 当访问不到t的属性的时候就访问__index的值的属性
    })

    return t

end

p1 = Person:new()
p2 = Person:new()
p1.name = "柯南"
p2.name = "小兰"

print(p1.name)  -- 柯南
print(p2.name)  -- 小兰

p1:eat()  -- 柯南吃饭
p2:eat()  -- 小兰吃饭

Student = Person:new()  -- Student继承Person
Student.sex = "男"

stu1 = Student:new()
print(stu1.name)  -- 张三 
print(stu1.sex)  -- 男

Lua 异常处理

assert 函数

assert首先检查第一个参数,若没问题,assert不做任何事情;否则,assert以第二个参数作为错误信息抛出。

local function add(a,b)
   assert(type(a) == "number", "a 不是一个数字")
   assert(type(b) == "number", "b 不是一个数字")
   return a+b
end
add(10)
--[[
lua: test.lua:3: b 不是一个数字
--]]

pcall 函数

有异常

function inputName(name)
    if #name > 8 then
        error("名字大于等于5,不合法")  -- 抛出异常
    end
    print("名字合法")
end


b,message = pcall(inputName,"真迪丽热巴")

print(b,message)  -- false    Test00.lua:3: 名字大于等于5,不合法

无异常

function inputName(name)
    if #name > 8 then
        error("名字大于等于5,不合法")
    end
    print("名字合法")
end


b,message = pcall(inputName,"真迪丽热")

print(b,message)
--[[
名字合法
true    nil
--]]

xpall 函数

xpall第二个参数为异常处理函数,发生异常时执行

function myfunction ()
   n = n/nil
end

function myerrorhandler( err )
   print( "ERROR:", err )
end

status = xpcall( myfunction, myerrorhandler )
print( status)
--[[
ERROR:    Test00.lua:2: attempt to perform arithmetic on global 'n' (a nil value)
false
--]]
Last modification:August 9th, 2020 at 04:20 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment