lua学习笔记

发布于:2022-11-28 ⋅ 阅读:(544) ⋅ 点赞:(0)

1、lua语言介绍

1. 1 语言介绍

  • Lua 是一个小巧的脚本语言。 其设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。

  • Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。

  • Lua并没有提供强大的库,这是由它的定位决定的。

  • 所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。

  • Lua由标准C编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。

  • 一个完整的Lua解释器不过200k,在所有脚本引擎中,Lua的速度是最快的。

  • 这一切都决定了Lua是作为嵌入式脚本的最佳选择。

速度快、跨平台、开源、即时编译。

1.2 lua语言应用场景

  • 游戏开发-用来做热更。
  • 独立软件应用脚本。 Photoshop
  • Web开发 应用脚本。
  • WEB服务器中间件。 nginx支持扩展,lua写个扩展

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ChmuNnvi-1665330518827)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20220925011711411.png)]

  • 扩展和数据库操作脚本插件如:MySQL Proxy 和 MySQL WorkBench

  • 缓存操作脚本。 秒杀系统,用lua把一个非原子操作变成原子操作、锁。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ti87pIWg-1665330518830)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20220925011617505.png)]

  • 安全系统,如入侵检测系统。

官网:https://www.lua.org/

百度百科:https://baike.baidu.com/item/lua

2、开发环境配置与hello world

2.1 开发环境配置

  • 下载源代码包。
  • 编译。
  • 测试版本:lua -v。

windows下载网址:https://www.lua.org/download.html

Lua是用纯ANSI实现的,可以在所有有ANSI C编译器的平台上不加修改地编译。Lua也可以像C++一样干净地编译。

Lua非常容易构建和安装。有详细说明但是这里有一个简单的终端会话,它下载当前版本的Lua并在Linux中编译它:

Linux下载编译指令:

curl -R -O http://www.lua.org/ftp/lua-5.4.4.tar.gz
tar zxf lua-5.4.4.tar.gz
cd lua-5.4.4
make all test

运行上面四条指令之后出现:lua与luac表明编译成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-38PyICIJ-1665330518831)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20220925020632089.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zzwRXbuc-1665330518831)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20220925020656065.png)]

lua是运行lua文件,luac与编译lua文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jNmGolqG-1665330518832)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20220925020740254.png)]

  • luac -o outfilename sourcefilename.lua 是给sourcefilename.lua起个名字叫 outfilename。

  • g++/gcc -o outfilename sourcefilename.cpp。

  • 两者非常类似。

2.2 输出hello world

只需要用打印函数print就可以:

Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> print (hello world)
stdin:1: ')' expected near 'world'
> print hello world
stdin:1: '=' expected near 'hello'
> print ('hello world')
hello world
> print ('hello world')

3、变量命名规范

--[[

变量命名规范:

  lua弱语言类型(动态语言类型),定义变量的时候,不需要类型修饰。

  而且,变量随时都可以改变。


  每行代码结束的时候,要不要分号都可以。


  变量名由数字、字母、下划线组成,不能由数字开头,也不能是保留字(关键字),而且也不可以是特殊字符。

  变量名区分大小写,避免创建下划线开头加大写字母组成的变量名。因为可能会有命名冲突 --VERSION 这就是lua里面的关键字。

变量类型

  变量分为三种类型,全局变量、局部变量、表字段

  默认创建的都是全局变量,局部变量用local修饰


代码块(作用域)

  do
    ....

 end

]] --

代码示例:

a=123     --int
print(a)

a="abc"  --string
print(a)

a=true   --bool
print(a)

a123=123
print(a123)
a_123=123
print(a_123)
_a=123
print(_a)
_123_a=123 
print(_123_a)
--这里由数字开头 123_a=123 --非法的

A123=456
print(A123)
abc=123
print(abc)
ABC=123
print(123)

local xyz=123 --局部变量
print(xyz)

--[[
{
    int a=123; 这里是C/C++语言中的作用域
}
]] --
abcd=123
--代码块
do
    xyz=123
    local XYZ=123
    print(abcd)
    print(xyz)
    print(XYZ)
end

print("\n")
print(abcd)
print(xyz)
print(XYZ)

输出结果:

PS D:\lua代码>  & 'c:/Users/hp/.vscode/extensions/actboy168.lua-debug-1.59.0-win32-x64/runtime/win32-x64/lua54/lua.exe' '-e' 'dofile[[c:/Users/hp/.vscode/extensions/actboy168.lua-debug-1.59.0-win32-x64/script/launch.lua]];DBG[[16296,ansi]]' 'D:\lua代码/变量命名规范/变量命名规范.lua'
123
abc 
true
123
123
123
123
456
123
123
123
123
123
123


123
123
nil
PS D:\lua代码> 

4、基本数据类型一

--[[

基本数据类型
    Lua 中有八种基本类型: nil、boolean、number、 string、function、userdata、 thread和table

    number 包括整型和浮点型

    string 单引号,双引号都表示string,不管是单引号,双引号,单个字符多个字符

    转义 用反斜杠 \n 换行符    \t 制表符
    原始输出用 [[  \] \]

    userdata 自定义数据格式
    thread   协程
    table   表

    在lua 中,只有false 和 nil 才表示假
    0和空串表示真


    库函数type返回一个描述给定值类型的字符串
    type (v)返回其唯一参数的类型,编码为字符串

]]--
a=nil
print(type(nil))

print(type(true))
print(type(boolean))

print(type(123))
print(type(111))
print(type(000))
print(type(1.123123))

--不管是单引号,双引号,单个字符多个字符
a='abc'
b='a'
c="abc"
d="a"
print(type(a))
print(type(b))
print(type(c))
print(type(d))

a='a\nb\ncd\tfd'
print(a)
b=[['a\nb\ncd\tfd']]
print(b)

print(type(type))
print(type(print))
print(type({}))

a1=nil

if a1 then
    print("真")
else
    print("假")
end

b1=0

if b1 then
    print("真")
else
    print("假")
end

c1=""

if c1 then
    print("真")
else
    print("假")
end


输出结果:

nil
boolean
nil
number
number
number
number
string
string
string
string
a
b
cd      fd
'a\nb\ncd\tfd'
function
function
table
假
真
真
PS D:\lua代码> 

5、基本数据类型二:function

--[[

    基本数据类型
        Lua有8种数据类型:nil,boolean,number,string,function,userdata,thread和table

        function 在lua中一个基本的数据类型是第一类值

        格式
        function funcName()
            ...
        end

            传参或返回值,都很自由

    function 可以作为参数被传递,也可以作为值被赋值给另一个变量
]] --
--c++
--void func(int a,int b){};
--void func(int a,int b){return a+b};

--lua
function Func1()
    print("this is func1")
end

Func1()

--传参
function Func2(a, b, c)
    print(a, b, c)
end

Func2(1, "lua", 'aa')

function Func3(a, b, c)
    print(1, 'a', "vb", 1, 3)
end

Func3()

--可变参
function Func5(...)
end

--函数当右值
--把匿名函数赋值给变量
Sum1 = function(a, b)
    return a + b
end

print(Sum1(10,20))

Sum2=Sum1

print(Sum2(100,200))

--当做其他函数的参数
function Func6(functionName,a,b)
    return functionName(a,b)
end

print(Func6(Sum1,1000,2000))

function Func7(a,b)
    return a+b
end

print(Func6(Func7,10000,20000))

输出结果:

this is func1
1       lua     aa
1       a       vb      1       3
30
300
3000
30000
PS D:\lua代码> 

6、基本数据类型三 : table

--[[

    基本数据类型
        Lua中有8中数据类型:nil boolean number string function table userdata thread

        table表,不是指数据库中的表,而是一种数据类型,类似于map,用k-v的方式实现,从理论上来讲,
        除了nil, 其他字符都可以作为k值(索引值)

        格式

        类似于hash
        TableName={
            k=v,
        }

        类似于数组 -- 下标从1开始
        TableName={
            x,
            y,
            z
        }

        可以用hash 格式和array 格式 混搭

        for 循环遍历
        for k,v in parirs(TableNmae) do
            print(k,v)
        end

        C++中的pairs是对组

        循环嵌套
        for k,v in paris(info3) do
            print(k,"==",v)
            if(type(v)=="table") then
                for k2,v2 in pairs(v) do
                    print("\t",k2,"==",v2)
                end
            end
        end
]]--

table中类似哈希 k-v 表示方式:

--哈希表示方式
local info1={
    id=123,
    name="yyw",
    grade=60,
    sex="male"
}

for k,v in pairs(info1) do
    print(k,v)
end

-- local id
-- local name
-- local grade
-- local sex
-- local info2={
--     [id]=123,
--     [name]="yyw",
--     [grade]=60,
--     [sex]="male"
-- }

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

print("\n")

print(info1.id)
print(info1.name)
print(info1["sex"])

print("\n")

--增加字段
info1.age=123
info1["country"]="china"
info1["province"]="shanghai"

for k,v in pairs(info1) do
    print(k,v)
end

--删除字段
info1["sex"]=nil
info1.age=nil


print("\n")

for k,v in pairs(info1) do
    print(k,v)
end

--修改字段
info1["name"]="yywnb"
info1.id=456

print("\n")

for k,v in pairs(info1) do
    print(k,v)
end

输出结果:

sex     male
grade   60
id      123
name    yyw


123
yyw
male


province        shanghai
id      123
name    yyw
sex     male
country china
grade   60
age     123


province        shanghai
id      123
name    yyw
country china
grade   60


province        shanghai
id      456
name    yywnb
country china
grade   60

table中类似数组表示方式:

--数组表示方式,并且lua中数组下标是从1开始的
local info2={
    "yyw",
    123,
    true
}

print("\n")

for k,v in pairs(info2) do
    print(k,v)
end

--修改字段
info2[2]=234
info2[1]="yywnb"

print("\n")

for k,v in pairs(info2) do
    print(k,v)
end

--增加字段
info2[100]="contry"
info2[50]="province"

print("\n")

for k,v in pairs(info2) do
    print(k,v)
end

--删除字段
info2[1]=nil

print("\n")

for k,v in pairs(info2) do
    print(k,v)
end

输出结果:

1       yyw
2       123
3       true


1       yywnb
2       234
3       true


1       yywnb
2       234
3       true
100     contry
50      province


2       234
3       true
100     contry
50      province
PS D:\lua代码> 

hash 格式和array 格式 混搭:

--[[

    基本数据类型
        Lua中有8中数据类型:nil boolean number string function table userdata thread

        table表,不是指数据库中的表,而是一种数据类型,类似于map,用k-v的方式实现,从理论上来讲,
        除了nil, 其他字符都可以作为k值(索引值)

        格式

        类似于hash
        TableName={
            k=v,
        }

        类似于数组 -- 下标从1开始
        TableName={
            x,
            y,
            z
        }

        可以用hash 格式和array 格式 混搭

        for 循环遍历
        for k,v in parirs(TableNmae) do
            print(k,v)
        end

        C++中的pairs是对组

        循环嵌套
        for k,v in paris(info3) do
            print(k,"==",v)
            if(type(v)=="table") then
                for k2,v2 in pairs(v) do
                    print("\t",k2,"==",v2)
                end
            end
        end
]]--

--哈希表示方式
local info1={
    id=123,
    name="yyw",
    grade=60,
    sex="male"
}

for k,v in pairs(info1) do
    print(k,v)
end

-- local id
-- local name
-- local grade
-- local sex
-- local info2={
--     [id]=123,
--     [name]="yyw",
--     [grade]=60,
--     [sex]="male"
-- }

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

print("\n")

print(info1.id)
print(info1.name)
print(info1["sex"])

print("\n")

--增加字段
info1.age=123
info1["country"]="china"
info1["province"]="shanghai"

for k,v in pairs(info1) do
    print(k,v)
end

--删除字段
info1["sex"]=nil
info1.age=nil


print("\n")

for k,v in pairs(info1) do
    print(k,v)
end

--修改字段
info1["name"]="yywnb"
info1.id=456

print("\n")

for k,v in pairs(info1) do
    print(k,v)
end

print("\n")

--数组表示方式,并且lua中数组下标是从1开始的
local info2={
    "yyw",
    123,
    true
}

print("\n")

for k,v in pairs(info2) do
    print(k,v)
end

--修改字段
info2[2]=234
info2[1]="yywnb"

print("\n")

for k,v in pairs(info2) do
    print(k,v)
end

--增加字段
info2[100]="contry"
info2[50]="province"

print("\n")

for k,v in pairs(info2) do
    print(k,v)
end

--删除字段
info2[1]=nil

print("\n")

for k,v in pairs(info2) do
    print(k,v)
end

print("\n")
print("\n")


local info3 = {
    name = "tom",
    age = 23,
    sex = "woman",
    100,
    "abc",
    { "xyz", "def", son1_k_1 = "son1-key-1" },
    son2 = {
        son2_k_1 = "son2-key-1",
        name = "son2", false,
        "abc-son2", 123456
    },
    country = "china",
    100000
}

for k,v in pairs(info3) do
    print(k,"==",v)
    if(type(v)=="table") then
        for k2,v2 in pairs(v) do
            print("\t",k2,"==",v2)
        end
    end
end
print("\n")
print("\n")

--修改字段
info3[3][2]="DEF"
info3[3]["son1_k_1"]="SON1-KEY-1"
info3["son2"][1]=true
info3["son2"][2]="ABC-SON2"

for k,v in pairs(info3) do
    print(k,"==",v)
    if(type(v)=="table") then
        for k2,v2 in pairs(v) do
            print("\t",k2,"==",v2)
        end
    end
end
print("\n")

--删除字段
info3[3]=nil
info3[4]=nil

for k,v in pairs(info3) do
    print(k,"==",v)
    if(type(v)=="table") then
        for k2,v2 in pairs(v) do
            print("\t",k2,"==",v2)
        end
    end
end

print("\n")

--增加字段
info3[5]=100000
info3["province"]="shanghai"
info3["son2"][4]="lua"

for k,v in pairs(info3) do
    print(k,"==",v)
    if(type(v)=="table") then
        for k2,v2 in pairs(v) do
            print("\t",k2,"==",v2)
        end
    end
end

输出结果:

1       ==      100
2       ==      abc
3       ==      table: 000002591D9262A0
                1       ==      xyz
                2       ==      def
                son1_k_1        ==      son1-key-1
4       ==      100000
sex     ==      woman
country ==      china
son2    ==      table: 000002591D926EA0
                1       ==      false
                2       ==      abc-son2
                3       ==      123456
                name    ==      son2
                son2_k_1        ==      son2-key-1
name    ==      tom
age     ==      23




1       ==      100
2       ==      abc
3       ==      table: 000002591D9262A0
                1       ==      xyz
                2       ==      DEF
                son1_k_1        ==      SON1-KEY-1
4       ==      100000
sex     ==      woman
country ==      china
son2    ==      table: 000002591D926EA0
                1       ==      true
                2       ==      ABC-SON2
                3       ==      123456
                name    ==      son2
                son2_k_1        ==      son2-key-1
name    ==      tom
age     ==      23


1       ==      100
2       ==      abc
sex     ==      woman
country ==      china
son2    ==      table: 000002591D926EA0
                1       ==      true
                2       ==      ABC-SON2
                3       ==      123456
                name    ==      son2
                son2_k_1        ==      son2-key-1
name    ==      tom
age     ==      23


1       ==      100
2       ==      abc
son2    ==      table: 000002591D926EA0
                1       ==      true
                2       ==      ABC-SON2
                3       ==      123456
                4       ==      lua
                name    ==      son2
                son2_k_1        ==      son2-key-1
sex     ==      woman
province        ==      shanghai
country ==      china
5       ==      100000
name    ==      tom
age     ==      23
PS D:\lua代码> 

7、基本运算符(一)

--[[

    赋值=
    lua中变量时弱类型,就是说明声明变量时候不需要类型,其类型取决于所赋的值,
    并且,同一个变量,可以随时切换成不同的数据类型

    多重赋值

    a,b=b,a 交换,类似c,c++中的swap

    算符运算符
    加+、减-、乘*,除/、取模%,指数(次方)^

    关系运算符
    等于==,不等于~=,大于>,小于<,大于或等于>=,小于或等于<=,
    关系运算符的运算结果只能是true 或者false,且只能在相同类型的数据间运算(运算时不会做隐式类型转换)

    对于对象型的数据(function,userdata,table),比较运算的知识引用,就是比较对象的地址
]]--
local a=1
local b=2
local c=a
print(a,b,c)
c="string"
print(c)
c=true
print(c)

local aa,bb,cc,dd=1,"abc",true
print(aa,bb,cc,dd)
aa,bb,cc=1,"true",{["aa"]=1,["bb"]=1}
print(aa,bb,cc)
tab={
    [aa]=1,
    [bb]=2
}
print(tab.aa,tab.bb)
tab={
    ["aa"]=1,
    ["bb"]=2
}
print(tab.aa,tab.bb)

local aaa,bbb=1,"string"
print(aaa,bbb)
local aaa,bbb=bbb,aaa
print(aaa,bbb)

print(10%3)
print(10%3.0)
print(10/3)
print(10/3.0)
print(2^10)

print(1==1)
print(1=="1")
print(1~="1")

local aaaa='1'
--print(1>="1") --err 语法错误
print(1+'1') --做了隐式类型转换
print(1+aaaa)

local tab3={
    ["ip"]="172.164",
    ["port"]="3306"
}
local tab2={
    ["name"]="lua"
}
print(tab3==tab2)
print(tab3.ip==tab2.name)
local tab4=tab3
print(tab4==tab3)
print(tab4==tab2)
print(tab3.ip==tab2.name)

输出结果:

1       2       1
string
true
1       abc     true    nil
1       true    table: 000002724B31A430
nil     nil
1       2
1       string
string  1
1
1.0
3.3333333333333
3.3333333333333
1024.0
true
false
true
2
2
false
false
true
false
false
PS D:\lua代码> 

8、基本运算符(二)

--[[
    逻辑运算符
        逻辑与 and、或 or、非 not
        && || !

        在lua中、逻辑运算与其他语言不是同一个意思、其运算结果返回值是参与运算的变量之一(not除外),
        not只返回true、false,在其他语言的逻辑运算、返回值是0或1(flase 或true),意思是返回一个bool值

        在lua中,只有nil(NULL,null)和false为假,其他都为真、包括空串或者0值

         对于and 和 or,实行短路运算(又称短路规则,短路求值,就是说,当前面的表达式可以返回时,就直接返回,不管后面的表达式)
]]--
--[[
    逻辑运算符
        逻辑与 and、或 or、非 not
        && || !

        在lua中、逻辑运算与其他语言不是同一个意思、其运算结果返回值是参与运算的变量之一(not除外),
        not只返回true、false,在其他语言的逻辑运算、返回值是0或1(flase 或true),意思是返回一个bool值

        在lua中,只有nil(NULL,null)和false为假,其他都为真、包括空串或者0值

         对于and 和 or,实行短路运算(又称短路规则,短路求值,就是说,当前面的表达式可以返回时,就直接返回,不管后面的表达式)
]]--

local a,b=1,2
print(a and b)  --如果a为真,则返回b

a=nil
print(a and b)  --如果a为假,则返回a
print("\n")

local c,d=1,false
print( c and d) --如果c为真,则返回d
c=nil
print(c and d)  --如果c为假,则返回c
print("\n")

local e,f=1,false
print(e or f)  --如果e为真,则返回e
e=nil
print(e or f)  --如果e为假,则返回f
print("\n")

local g,h=1,2
--print(g not h)  --错误写法
print(not g)      --如果e为真,则返回假
print(not h)      --如果h为真,则返回假

local i= false
print(not i)      --如果i为假,则返回真
print("\n")

--用逻辑运算符or设置值
function func1(a,b)
    a= a or b
    b= a or b
    print(a,b)
end

func1()
func1(10,20)
print("\n")

--实现三目运算符 a=b?c:d 如果b=a为真,则值就是c,否则为d
local x,y,z=1,2,3
local u=(x and y) or z
print(u)

x=false
u=(x and y) or z
print(u)
print("\n")

local A,B,C=1,2,3
B=false
local D=(A and B) or C
print(D)

local E=((A and {B}) or {C})[1]  --这里才是一个合格的三目运算符
print(E)

输出结果:

2
nil


false
nil


1
false


false
false
true


nil     nil
10      10


2
3


3
false
PS D:\lua代码> 

9、流程控制 - 判断

--[[

    流程控制语句--判断
    if condition then
        ...
    end
]]--
local a,b=1,2
if(a==1) then
    print(a)
else
    print(b)
end

if a>=1 then
    print(b)
else
    print(a)
end

if a<1 then
    print("a<1")
else
    print("a>b")
end

local grade,score=60,90
if grade>=60 then
    print("well")
elseif grade<60 then
    print("fail")
elseif grade>60 and grade>=90 then
    print("good")
end

grade,score=60,90
if grade>60 then
    print("well")
elseif grade<60 then
    print("fail")
elseif grade>=60 and score>=90 then
    print("good")
end

if grade< 60 then
    print("fail")
elseif grade>=65 and grade<70 then
    print("well")
else
    if score>=90 then
        print("good")
    end
end

输出结果:

1
2
a>b
well
good
good
PS D:\lua代码> 

10、流程控制循环(一) ->while和repeat

--[[

    流程控制语句 循环

    while condition do
        statements
    end

    没有continue

    break只能跳出一次循环,跳出当前while循环语句

    goto FLAG 语句跳转到指定标记FLAG处,也可以用于跳出循环,FLAG是一个标记位,相当于一个锚点

    两者区别是
        break 只能跳出当前循环,而goto 可以跳转到指定位置,这样可以忽略一些代码

    在lua 中,没有这些运算符 i++, i--, ++i, --i, +=, -=

    a=b=1,lua中这种写法是错误的写法

    repeat
        --statements
    until condition

    while 和 repeat的区
        while循环中当条件不成立跳出循环
        repeat当条件成立时跳出循环
]]--
--求和1+...+100
--local i,sum=nil,nil
local i,sum=0,0
while i<100 do
    i=i+1
    sum=sum+i
end
print(i,sum)

i,sum=0,0
while i<100 do
    if i>49 then
        break
    end
    i=i+1
    sum=sum+i
end
print(i,sum)

i,sum=0,0
while i<100 do
    if i>49 then
        --break
        goto FLAG
    end
    i=i+1
    sum=sum+i
end

print(i,sum)

::FLAG::
print("lua")

i,sum=0,0
repeat
    i=i+1
    sum=sum+i
until i>=100

print(i,sum)

输出结果:

100     5050
50      1275
lua
100     5050
PS D:\lua代码> 

11、流程控制循环goto语句

--[[

    流程控制语句

    goto FLAG 语句,跳转到指定标记处,也可以用于跳出循环,FLAG是一个标记位,相当于一个锚点
    {
        int a;
    }
    代码块,作用域{}
    do
        ...
    end

    flag不可见原则
        1、不能从外面goto到代码块里面,因为代码块里面的flag对于外面的goto语句来说是不可见的
        2、不能跳出或者跳入一个函数,因为函数也是一个block块
        3、不能跳入本地变量(local)作用域
]]--

--死循环
-- local i=0
-- ::FLAG10::
--     print(i)
--     i=i+1
-- goto FLAG10
print(1234)
goto FLAG13
--goto FLAG11  --err 标签`FLAG11`不可见
--goto FLAG13  --err 标签`FLAG12`不可见
do
    print("a")
    print("b")
    ::FLAG11::
    print("c")
    print("d")
    ::FLAG12::
end

::FLAG13::
print("aa")
print("bb")
print("cc")
::FLAG14::
print("dd")
print("\n")


function func1()
    ::FLAG3::
    print( "func1-11")
    print( "func1--22")
    --goto FLAG6 err 标签`FLAG6`不可见
    goto FLAG5
    ::FLAG4::

    print("func1--33")
    ::FLAG5::
end

-- goto FLAG3 err 标签`FLAG3`不可见
::FLAG6::
print("aaaa")
func1()
-- goto FLAG4   --FLAG4 err 标签`FLAG3`不可见
::FLAG7::
print("bbbb")


do
    a=123
    ::FLAG1::
    --print(a)
    ::FLAG2::
    --goto FLAG3    --此处goto是在local b的作用域外面,使用报错
    local b=456
    ::FLAG3::
    --print(b)
    goto FLAG3   --此处goto是在local b的作用域里面
end
--print(a)
--print(b)

输出结果:

1234
aa  
bb  
cc  
dd  
    
    
aaaa
func1-11
func1--22
bbbb

12、流程控制循环(二) ->数值for和范围for ipairs

--[[
    流程控制语句

    for循环分为数值循环和泛型循环(类似foreach)

    数值循环
    for 变量名=初始值,结束值,步长(默认为1,可以省略) do
        ...
    end

    泛型循环
    for k,v in 迭代函数(table) do  --此处v可以省略,k不能省略
        ...
    end

    迭代函数 pairs(table) ipairs(table)

    ipairts(table) 顺序遍历,中间的序号会断开,遇到 k==v 直接跳过
    ,遇到第一个nil会终止遍历,一般情况下,ipairs(table)用于数组
    类型的集合遍历
]]--

for i=1,10 do
    io.write(i," ")
end
print("\n")

for i=1,10,2 do
    io.write(i," ")
end
print("\n")

for i=1,10,3 do
    io.write(i," ")
end
print("\n")

for i=10,1,-2 do
    io.write(i," ")
end
print("\n")

local tab1={
    11,
    22,
    33,
    44,
    55
}
for i=1,#tab1 do
    io.write(tab1[i]," ")
end
io.write("\n")

for i=1,10 do
    print(tab1[i])
end
io.write("\n")


tab2={
    11,
    22,
    "xxx",
    44,
    ["key"]=vvv,
    "nil",
    key2="vvv",
    nil,
    55
}
for k,v in ipairs(tab2) do
    print(k,v)
end

-- for k,v in ipairs(tab2) do
--     tab2[k]=v*10
-- end
for k,v in ipairs(tab2) do
    print(k,v)
end

输出结果:

1 2 3 4 5 6 7 8 9 10 

1 3 5 7 9

1 4 7 10

10 8 6 4 2

11 22 33 44 55
11
22
33
44
55
nil
nil
nil
nil
nil

1       11
2       22
3       xxx
4       44
5       nil
1       11
2       22
3       xxx
4       44
5       nil
PS D:\lua代码> 

13、流程控制循环(三) -> pairs

--[[
    流程控制语句

    for循环分为数值循环和泛型循环(类似foreach)

    数值循环
    for 变量名=初始值,结束值,步长(默认为1,可以省略) do
        ...
    end

    泛型循环
    for k,v in 迭代函数(table) do  --此处v可以省略,k不能省略
        ...
    end

    迭代函数 pairs(table) ipairs(table)

    ipairts(table) 顺序遍历,中间的序号会断开,遇到 k==v 直接跳过
    ,遇到第一个nil会终止遍历,一般情况下,ipairs(table)用于数组
    类型的集合遍历,而且遍历的是数组下标从1开始不能是负数下标,
    如果是负数下标的话跳过负数下标

    pairs(tbale) 遇到nil会跳过,同时适用于数组类型和 k==v 类型的集合,
    混搭没有问题,如果是混搭的时候,会优先获取数组类型数据

    paris的适用范围大于ipairs,类似iparis是pairs的子集
    如果使用时不确定选择哪个,无脑pairs

]]--
local tab1={
    11,
    nil,
    "abc",
    22,
    "lua",
    33,
    44,
    55
}

for k,v in pairs(tab1) do
    print(k,v)
end
print("\n")

local tab2={
    11,
    id=123,
    22,
    name="tom",
    age=456,
    33,
    conuty="china",
    nil,
    "abc",
    44,
    ["province"]="shanghai",
    55
}
for k,v in pairs(tab2) do
    print(k,v)
end

for i=1,9 do
    for j=1,i do
        io.write(i,"*",j,"=",i*j,"\t")
    end
    print("\n")
end

输出结果:

1       11
3       abc
4       22
5       lua
6       33
7       44
8       55


1       11
2       22
3       33
5       abc
6       44
7       55
conuty  china
name    tom
province        shanghai
id      123
age     456
1*1=1

2*1=2   2*2=4

3*1=3   3*2=6   3*3=9

4*1=4   4*2=8   4*3=12  4*4=16

5*1=5   5*2=10  5*3=15  5*4=20  5*5=25

6*1=6   6*2=12  6*3=18  6*4=24  6*5=30  6*6=36

7*1=7   7*2=14  7*3=21  7*4=28  7*5=35  7*6=42  7*7=49

8*1=8   8*2=16  8*3=24  8*4=32  8*5=40  8*6=48  8*7=56  8*8=64

9*1=9   9*2=18  9*3=27  9*4=36  9*5=45  9*6=54  9*7=63  9*8=72  9*9=81

PS D:\lua代码> 

14、table -> array

--[[
    table
        array
        hash
]]--
--自定义下标不建议这样使用
local tab1={
    11,
    22,
    33,
    "string"
}
tab1[55]="lua"

for k,v in ipairs(tab1) do
    print(k,v)
end
print("\n")
for k,v in pairs(tab1) do
    print(k,v)
end
print("\n")

local tab2={}
for i=-4,4 do
    tab2[i]=i*10
end

for k,v in ipairs(tab2) do  --遍历的是数组下标从1开始不能是负数下标,如果是负数下标的话跳过负数下标
    print(k,v)
end
print("\n")
for k,v in pairs(tab2) do
    print(k,v)
end
print("\n")

--二维数组
local x={
    11,
    22,
    33
}
local y={
    44,
    55,
    66
}
local tab3={
    {
        11,
        22,
        33
    },
    {
        44,
        55,
        66
    }
}

for i=1,2 do
    for j=1,3 do
        print(i,j,tab3[i][j])
    end
    print("\n")
end
print("\n")

for k,v in ipairs(tab3) do
    for k2,v2 in ipairs(v) do
        print(k,k2,v2)
    end
    print("\n")
end
print("\n")

for k,v in pairs(tab3) do
    for k2,v2 in pairs(v) do
        print(k,k2,v2)
    end
    print("\n")
end
print("\n")

--自己创建一个二维数组
local tab4={}
for i=1,3 do
    tab4[i]={}  --因为是二维数组,所以tab4[i]={}一定要
    for j=1,2 do
        tab4[i][j]=i*j*10
    end
end

for k,v in ipairs(tab4) do
    for k2,v2 in ipairs(v) do
        print(k,k2,v2)
    end
    print("\n")
end

输出结果:

1       11
2       22
3       33
4       string


1       11
2       22
3       33
4       string
55      lua


1       10
2       20
3       30
4       40


1       10
2       20
3       30
4       40
-2      -20
-1      -10
-4      -40
-3      -30
0       0


1       1       11
1       2       22
1       3       33


2       1       44
2       2       55
2       3       66




1       1       11
1       2       22
1       3       33


2       1       44
2       2       55
2       3       66




1       1       11
1       2       22
1       3       33


2       1       44
2       2       55
2       3       66




1       1       10
1       2       20


2       1       20
2       2       40


3       1       30
3       2       60


PS D:\lua代码> 

15、table操作函数

--[[
    ---@param list table
    ---@param sep? string
    ---@param i?   integer
    ---@param j?   integer
    ---@return string
    ---@nodiscard
    function table.concat(list, sep, i, j) end
    ---提供一个列表,其所有元素都是字符串或数字,返回字符串 `list[i]..sep..list[i+1] ··· sep..list[j]`。

    ---@param list table
    ---@param pos? integer
    ---@return any
    function table.remove(list, pos) end
    ---移除 `list` 中 `pos` 位置上的元素,并返回这个被移除的值。默认不写pos是最后一个位置


    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-table.insert"])
    ---
    ---@overload fun(list: table, value: any)
    ---@param list table
    ---@param pos integer
    ---@param value any
    function table.insert(list, pos, value) end
    ---在 `list` 的位置 `pos` 处插入元素 `value`。


    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-table.sort"])
    ---
    ---@generic T
    ---@param list T[]
    ---@param comp? fun(a: T, b: T):boolean
    ---function table.sort(list, comp)
    ---在表内从 `list[1]` 到 `list[#list]` *原地* 对其间元素按指定次序排序。


    ---将元素从表 `a1` 移到表 `a2`。
    ---```lua
    ---a2[t],··· =
    ---a1[f],···,a1[e]
    ---return a2
    ---```
    ---
    ---
    ---
    ---@param a1  table
    ---@param f   integer
    ---@param e   integer
    ---@param t   integer
    ---@param a2? table
    ---@return table a2
    function table.move(a1, f, e, t, a2) end
]]--

--table.concat(table, sep, i, j),将表中的元素拼接成一个字符串,sep是连接符号,i是起始位置,j是终止位置
--table.remove(list, pos)移除 `list` 中 `pos` 位置上的元素,并返回这个被移除的值。默认不写pos是最后一个位置
--table.insert(list, pos, value) 在 `list` 的位置 `pos` 处插入元素 `value`。默认不写pos是在最后一个位置插入
--table.sort(list, comp) 数组排序,默认从小到大,可自定义排序规则
--table.move(a1, f, e, t, a2) 把数组a1中的元素往a2中拷贝
local tab1={
   "abc",
   "DEF",
   "123",
   11,
   ["key"]=value,
   ["id"]=123
}
print(table.concat(tab1))
print(table.concat(tab1,"--"))
print(table.concat(tab1,"--",2,3))
print(table.concat(tab1,"--",1,2))
print("\n")

print(table.remove(tab1))
print(table.concat(tab1))
print(table.remove(tab1,1))
print(table.concat(tab1))
print("\n")

print(table.insert(tab1,"china"))
print(table.concat(tab1))
print("\n")

table.sort(tab1)
print(table.concat(tab1))
print("\n")

function cmp(a,b)
    return a>b
end
table.sort(tab1,cmp)
print(table.concat(tab1))
print("\n")

local tab2={
    "a",
    "b"
}

table.move(tab1,1,2,3,tab2)  --3指定是第二个表tab2中的位置,这里就是第三个位置,1和2代表tab1中的第一个元素开始和结束就是chinaDEF
print(table.concat(tab1))
print(table.concat(tab2))
print("\n")


--下面的两种写法表示一样的效果,没区别
local info1={
    id=123,
    name="lua",
    grade=60,
    sex="male"
}
local info2={
    ["id"]=123,
    ["name"]="lua",
    ["grade"]=60,
    ["sex"]="male"
}

输出结果:

abcDEF12311
abc--DEF--123--11
DEF--123
abc--DEF


11
abcDEF123
abc
DEF123



DEF123china


123DEFchina


chinaDEF123


chinaDEF123
abchinaDEF


PS D:\lua代码> 

16、string常用api

--[[
    ---
    ---接收一个字符串,将其中的小写字符都转为大写后返回其副本。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-string.upper"])
    ---
    ---@param s string
    ---@return string
    ---@nodiscard
    function string.upper(s) end

    return string

    ---
    ---将其中的大写字符都转为小写后返回其副本。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-string.lower"])
    ---
    ---@param s string
    ---@return string
    ---@nodiscard
    function string.lower(s) end

    return string

    ---
    ---返回其长度。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-string.len"])
    ---
    ---@param s string
    ---@return integer
    ---@nodiscard
    function string.len(s) end

    return number

    ---
    ---返回字符串 s 的翻转串。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-string.reverse"])
    ---
    ---@param s string
    ---@return string
    ---@nodiscard
    function string.reverse(s) end

    return string

    ---
    ---返回字符串的子串, 该子串从 `i` 开始到 `j` 为止。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-string.sub"])
    ---
    ---@param s  string
    ---@param i  integer
    ---@param j? integer
    ---@return string
    ---@nodiscard
    function string.sub(s, i, j) end

    return string

    ---
    ---查找第一个字符串中匹配到的 `pattern`(参见 [§6.4.1](command:extension.lua.doc?["en-us/54/manual.html/6.4.1"]))。
    ---
    ---
    ---@param s       string
    ---@param pattern string
    ---@param init?   integer
    ---@param plain?  boolean
    ---@return integer start
    ---@return integer end
    ---@return any ... captured
    ---@nodiscard
    function string.find(s, pattern, init, plain) end

    return number

    ---
    ---将字符串 s 中,所有的(或是在 n 给出时的前 n 个) pattern (参见 [§6.4.1](command:extension.lua.doc?["en-us/54/manual.html/6.4.1"]))都替换成 repl ,并返回其副本。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-string.gsub"])
    ---
    ---@param s       string
    ---@param pattern string
    ---@param repl    string|number|table|function
    ---@param n?      integer
    ---@return string
    ---@return integer count
    ---@nodiscard
    function string.gsub(s, pattern, repl, n) end

    return string

    ---
    ---接收零或更多的整数。 返回和参数数量相同长度的字符串。 其中每个字符的内部编码值等于对应的参数值。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-string.char"])
    ---
    ---@param byte integer
    ---@param ... integer
    ---@return string
    ---@nodiscard
    function string.char(byte, ...) end

    return string

    ---
    ---返回字符 `s[i]`, `s[i+1]`, ... ,`s[j]` 的内部数字编码。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-string.byte"])
    ---
    ---@param s  string
    ---@param i? integer
    ---@param j? integer
    ---@return integer ...
    ---@nodiscard
    function string.byte(s, i, j) end

    return number
]]--
local str1="abCD1234"
local str2=string.upper(str1)  --接收一个字符串,将其中的小写字符都转为大写后返回其副本。
print(str2)
local str3=string.lower(str1)  --接收一个字符串,将其中的大写字符都转为小写后返回其副本。
print(str3)
print("\n")

print(string.len(str1))        --接收一个字符串,返回其长度。
print(string.len("你好1bc a")) --其中一个汉字是3个字符,总共11个字符
print("\n")

print(string.reverse(str1))    --接收一个字符串,返回字符串 s 的翻转串。
print("\n")

local a=0000
print("china".." is".." best".." country!".." str1"..1 ..1 ..a..2)               -- .. 连接符,number 如果在连接符左边,后面要空格
print("\n")

print(string.sub(str1,2))      --返回字符串的子串, 该子串从 `i` 开始到 `j` 为止。默认有两个参数
print(string.sub(str1,1,2))
print(string.sub(str1,-4,-2))  --负数是从右往左数
print("\n")

print(string.find(str1,1))        -- 在字符串s中 查找pattern(可以是表达式),如果找到,则返回pattern 第一次出现的开始位置和结束位置,如果没找到,返回nil
print(string.find(str1,"abC"))
print(string.find(str1,"ab",3))      --从指定位置开始寻找
print(string.find(str1,"ab",3,5))
print(string.find(str1,"ab",-8))     --负数是从右往左数
print("\n")

print(string.find(str1,"%d",1));     --最后一个参数,默认就是false,开启正则匹配模式,这里寻找的是%d number型
print(string.find(str1,"(%d%d%d%d)"))
print(string.find(str1,"%d",6,true));--最后一个参数,true 是直接当子串处理
print("\n")

print(string.gsub(str1,1,2))         --全部替换
print(string.gsub(str1,"ab","AB",2)) --最多替换两次
print("\n")

print(string.char(65))               --asscil码转字符,数字只能是0~255
print(string.byte("a"))              --字符转asscil码
print(string.byte("abc",1,2));       --1和2代表转几个
print(string.byte("abc",1,3));

输出结果:

ABCD1234
abcd1234


8
11


4321DCba


china is best country! str11102


bCD1234
ab
123


5       5
1       3
nil
nil
1       2


5       5
5       8       1234
nil


abCD2234        1
ABCD1234        1


A
97
97      98
97      98      99
PS D:\lua代码> 

17、function常见表示形式

--[[

    function进阶,常见的表现形式
        --函数普通函数

        --函数当右值,赋值给一个变量

        --匿名函数当右值

        --函数当参数

        --函数当返回值

        --匿名函数当返回值

        --函数多返回值
]]--
--普通函数
function show(a,b)
    print(a,b)
end
--函数直接调用
show("abc",123)

--函数当右值,赋值给一个变量
func1=show
func1("abc",123)

--匿名函数当右值
func2=function(a,b)
    print("abc",123)
end
func2()

--函数当参数
function func3(func,a,b)
    func(a,b)
end
func3(show,"abc",123)
func3(
    function(a, b)
        print(a, b)
    end, 
    "abc", "123"
)
print("\n")

--函数当返回值
function func4()
    return func1
end
fun1=func4()
fun1("abc",123)
func4()("abc",123)

--匿名函数当返回值
function func5()
    return function(a,b)
        print(a,b)
    end
end
fun2=func5()
fun2("abc",123)
func5()("abc",123)
print("\n")

--函数多返回值
function func6()
    function func6_son1(a,b)
        print("son1",a,b)
    end
    function func6_son2(a,b)
        print("son2",a,b)
    end
    return func6_son1,
    func6_son2,
    func1,
    function(a,b)
        print("son_last",a,b)
    end
end

fun3,fun4,fun5,fun6=func6()
fun4("abc",123)
fun5("abc",123)
fun6("abc",123)
fun3("abc",123)

输出结果:

abc     123
abc     123
abc     123
abc     123
abc     123


abc     123
abc     123
abc     123
abc     123


son2    abc     123
abc     123
son_last        abc     123
son1    abc     123
PS D:\lua代码> 

18、table中的function

--[[

    function进阶,table中的function

]]--

function show(a)
    print(a)
end

local tab1={
    t1_show=show,
    add=function(a,b)
        return a+b
    end
}
tab1.sub=function(a,b)
    return a-b
end
tab1["mul"]=function(a,b)
    return a*b
end
tab1["div"]=function(a,b)
    return a/b
end

tab1.t1_show("abc")
tab1.t1_show(tab1.add(11,22))
tab1["t1_show"](123)
tab1.t1_show(tab1.sub(11,22))
tab1.t1_show(tab1.mul(11,22))
tab1.t1_show(tab1.div(11,22))
print("\n")

calc={
    add=function(a,b)
        return a+b
    end,
    sub=function(a,b)
        return a-b
    end,
    mul=function(a,b)
        return a*b
    end,
    sub=function(a,b)
        return a/b
    end,
    show=function(a)
        print(a)
    end
}

calc.show(calc.add(11,22))
calc.show(calc.sub(11,22))
calc.show(calc.mul(11,22))
calc.show(calc.sub(11,22))

tab2={
    a=0,
    b=0,
    result=0,
    add=function()
        tab2.result=tab2.a+tab2.b
    end,
    sub=function()
        tab2.result=tab2.a-tab2.b
    end,
    mul=function()
        tab2.result=tab2.a*tab2.b
    end,
    div=function()
        tab2.result=tab2.a/tab2.b
    end,
    show=function()
        print (tab2.result)
    end
}

tab2.a=11
tab2.b=22
tab2.add()
tab2.show()

输出结果:

abc
33
123
-11
242
0.5


33
0.5
242
0.5

33
0.5
242
0.5
33

19、用select处理可变参数

--[[

    关于用select处理可变参数

    select(n,...),表示获取可变参数的一部分数据从n开始
    select("#",...),表示获取可变参数的个数

     ---
    ---接收任意数量的参数,并将它们的值打印到 `stdout`。 它用 `tostring` 函数将每个参数都转换为字符串。 
    --`print` 不用于做格式化输出。仅作为看一下某个值的快捷方式。 多用于调试。 
    --完整的对输出的控制,请使用 [string.format](command:extension.lua.doc?["en-us/54/manual.html/pdf-string.format"])
    -- 以及 [io.write](command:extension.lua.doc?["en-us/54/manual.html/pdf-io.write"])。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-print"])
    ---

    ---
    ---如果 `index` 是个数字, 那么返回参数中第 `index` 个之后的部分; 负的数字会从后向前索引(`-1` 指最后一个参数)。 
    --否则,`index` 必须是字符串 `"#"`, 此时 `select` 返回参数的个数。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-select"])
    ---
    ---@param index integer|'"#"'
    ---@return any
    ---@nodiscard
    function select(index, ...) end

]]--
local function func1(...)
    print(select(1,...))
    print(select(2,...))
    print(select(3,...))
    print(select(4,...))
    print(select(5,...))
    print(select("#",...))

    local sum=0
    local count=select("#",...)
    for i=1,count do
        sum=sum+(select(i,...))
    end
    return sum
end

print(func1(11,22,33,44,55))
print("\n")

local function func2()
    return 11,22,33
end

local function func3()
    return 111,222,333
end

print(func2())
print(func3())
print("\n")
print((func2()))
print((func3()))
print("\n")
print(func2(),"===",func3())
print((func2()),"===",func3())

输出结果:

11      22      33      44      55
22      33      44      55
33      44      55
44      55
55
5
165


11      22      33
111     222     333


11
111


11      ===     111     222     333
11      ===     111     222     333
PS D:\lua代码> 

20、用pack处理可变参数

--[[

    ---用pack 处理函数可变参数
    ---table.pack(...) 将可变参打包成一个table,且会在最后多出一个n键,其对应的值是可变参的参数个数
    ---table.unpack(list, i, j) 解包,将table 解成可变参

    ---
    ---返回用所有参数以键 `1`,`2`, 等填充的新表, 并将 `"n"` 这个域设为参数的总数。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-table.pack"])
    ---
    ---@return table
    ---@nodiscard
    function table.pack(...) end

    ---
    ---返回列表中的元素。 这个函数等价于
    ---```lua
    ---    return list[i], list[i+1], ···, list[j]
    ---```
    ---i 默认为 1 ,j 默认为 #list。
    ---
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-table.unpack"])
    ---
    ---@generic T
    ---@param list T[]
    ---@param i?   integer
    ---@param j?   integer
    ---@return T   ...
    ---@nodiscard
    function table.unpack(list, i, j) end
]]--
local function func1(...)
    print(table.pack(...).n)

    local sum=0
    local tab1=table.pack(...)
    for k,v in pairs(tab1) do
        if k~="n" then
            sum=sum+v
        end
    end

    local count=tab1.n
    print(count)
    for i=1,count do
        if tab1[i]~=nil then
            sum=sum+tab1[i]
        end
    end

    return sum
end

print(func1(11,22,nil,33,44,55,nil))
print("\n")

local function func2()
    --return 11,22,33,44,55,66
    return table.pack(11,22,33,44,55,66)
end

print(11,22,33,44,55,66)
print(func2())
print(table.unpack(func2()))
print(table.unpack(func2(),1,2))
print("\n")

local tab2=func2()
print(table.unpack(tab2))
print("\n")

---可变参转成table
local function func3(...)
    local sum=0
    local tab1={...}
    local count=#tab1
    print(count)
    for k,v in pairs(tab1) do
        if k~="nil" then
            sum=sum+v
        end
        print(k,v)
    end
    return sum
end

print(func3(11,22,nil,33,44,nil,55,66,77))

输出结果:

7
7
330


11      22      33      44      55      66
table: 000001926BF91DE0
11      22      33      44      55      66
11      22


11      22      33      44      55      66


9
1       11
2       22
4       33
5       44
7       55
8       66
9       77
308
PS D:\lua代码> 

21、lua中闭包 (closure)的实现

--[[

    闭包

    百度百科 https://baike.baidu.com/item/闭包/10908873

    闭包就是能够读取其他函数内部变量的函数。
    例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。
    在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

    lua
    当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,
    这种特征在lua中我们称作词法定界。
    虽然这看起来很清楚,事实并非如此。
    词法定界加上第一类函数在编程语言里是一个功能强大的概念,很少语言提供这种支持。

    个人理解
    闭包就是指一种编码方式,一种思想,而不是指某种具体的技术或函数或库

    内部函数读取外部函数的变量

    lambda 表达式,经典的闭包表现方式之一

]]--

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8m1N0Ihi-1665330518835)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221004001207777.png)]

local function func1(a,b)
    local x=0
    local y=0
    return function()
        x=x+1           --类似static属性,内部匿名函数访问外部函数func1的变量x
        print(a,b,x,y)
    end
end

func1("abc",123)()
print("\n")

local tmp1=func1("abc",123)
tmp1()
tmp1()
tmp1()
tmp1()
print("\n")

local tmp2=func1("abc",123)
tmp2()

输出结果:

abc     123     1       0


abc     123     1       0
abc     123     2       0
abc     123     3       0
abc     123     4       0


abc     123     1       0
PS D:\lua代码> 

22、用闭包 (closure)实现ipairs迭代器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NaoNqpue-1665330518835)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221004002633436.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f2KRhXAO-1665330518836)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221004003844556.png)]

--[[

    迭代器

    百科百科 https://baike.baidu.com/item/迭代器

    迭代器(iterator)有时又称光标(cursor)是程序设计的软件设计模式,
    可在容器对象(container,例如链表或数组)上遍访的接口,
    设计人员无需关心容器对象的内存分配的实现细节。


    ---
    ---如果 `t` 有元方法 `__pairs`, 以 `t` 为参数调用它,并返回其返回的前三个值。
    ---
    ---否则,返回三个值:`next` 函数, 表 `t`,以及 `nil`。 因此以下代码
    ---```lua
    ---    for k,v in pairs(t) do body end
    ---```
    ---能迭代表 `t` 中的所有键值对。
    ---
    ---参见函数 [next](command:extension.lua.doc?["en-us/54/manual.html/pdf-next"]) 中关于迭代过程中修改表的风险。
    ---
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-pairs"])
    ---
    ---@generic T: table, K, V
    ---@param t T
    ---@return fun(table: table<K, V>, index?: K):K, V
    ---@return T
    function pairs(t) end

    ---
    ---返回三个值(迭代函数、表 `t` 以及 `0` ), 如此,以下代码
    ---```lua
    ---    for i,v in ipairs(t) do body end
    ---```
    ---将迭代键值对 `(1,t[1]) ,(2,t[2]), ...` ,直到第一个空值。
    ---
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-ipairs"])
    ---
    ---@generic T: table, V
    ---@param t T
    ---@return fun(table: V[], i?: integer):integer, V
    ---@return T
    ---@return integer i
    function ipairs(t) end
]]--
local tab1={
    11,
    22,
    33,
    nil,
    ["string"]="lua",
    44,
    nil,
    55
}

for k,v in pairs(tab1) do
    print(k,v)
end
print("\n")

for k,v in ipairs(tab1) do
    print(k,v)
end
print("\n")

local function MyIpairs(tab1)
    local count=#tab1
    local index=0

    return function()
        if index <= count then
            index=index+1
            if tab1[index]~=nil then
                return index,tab1[index]
            end
        end
    end
end

local function MyPairs(tab1)
    local count=#tab1
    local index=0

    return function()
        if index<=count then
            index=index+1
            -- if tab1[index]~=nil then
            --     return index,tab1[index]
            -- else
            --     index=index+1
            --     return index,tab1[index]
            -- end
            if tab1[index] == nil then
                index=index+1
            end
            return index,tab1[index]
        end
    end
end


for k,v in MyIpairs(tab1) do
    print(k,v)
end
print("\n")

for k,v in MyPairs(tab1) do
    print(k,v)
end

输出结果:

1       11
2       22
3       33
5       44
7       55
string  lua


1       11
2       22
3       33


1       11
2       22
3       33


1       11
2       22
3       33
5       44
7       55
9       nil
PS D:\lua代码> 

23、元表和元方法(一)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2E3VOrse-1665330518837)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221004213130240.png)]

--[[

    元表 metatable

        元表并不是一个普通的表,而是一套自定义的计算规则,用这些规则,可以实现表与表之间的运算
        而这些规则,都以函数的方式,写在元表中,所以又称为元方法(就是写在元表里面的方法)

        起到一个类似于其它语言中的运算符重载的作用


        setmetatable(table, metatable) -- 将 metatable 设为 table 的元表,其返回值为table

        ---
        ---给指定表设置元表。 (你不能在 Lua 中改变其它类型值的元表,那些只能在 C 里做。
        ---如果 `metatable` 是 `nil`, 将指定表的元表移除。 如果原来那张元表有 `"__metatable"` 域,抛出一个错误。
        ---
        ---
        ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-setmetatable"])
        ---
        ---@param table      table
        ---@param metatable? table
        ---@return table
        function setmetatable(table, metatable) end
]]--
local tab1={
    11,
    22,
    33,
    44
}
local tab2={
    111,
    222,
    333,
    nil,
    444,
    nil
}
local tab11={
    11,
    22,
    33,
    44
}
local tab22={
    111,
    222,
    333,
    nil,
    444,
    nil
}
local metaTab3={}
setmetatable(tab1,metaTab3)
setmetatable(tab2,metaTab3)
local tab5={
    "china ",
    "best ",
    "country ",
    1
}
local tab6={
    "is ",
    "of ",
    2
}
local metaTab4={}
setmetatable(tab5,metaTab4)
setmetatable(tab6,metaTab4)


metaTab3["__add"]=function (t1,t2)
    local res={}
    local len1=#t1
    local len2=#t2
    if len1 >len2 then
        len2=len1
    end
    for i=1,len2 do
        -- res[i]=t1[i]+t2[i]
        local a=t1[i] or 0
        local b=t2[i] or 0
        res[i]=a+b
    end
    return res
end

metaTab4.__add=function(t1,t2)
    local res={}
    local len1=#t1
    local len2=#t2
    if len1> len2 then
        len2=len1
    end
    for i=1,len2 do
        local a=t1[i] or ""
        local b=t2[i] or ""
        res[i]=a..b
    end
    return res
end

local metaTab5={}
setmetatable(tab11,metaTab5)
setmetatable(tab22,metaTab5)
metaTab5["__eq"]=function(t1,t2)
    if #t1~=t2 then
        return false
    end
    for i=1,#t1 do
        if t1[i] ~= t2[i] then
            return false
        end
    end
    return true
end

local tab3=tab1+tab2
print(tab3)
for k,v in ipairs(tab3) do
    print(k,v)
end
print("\n")

local tab4=tab5+tab6
print(tab4)
print(tab5)
print(tab6)
for k,v in ipairs(tab4) do
    print(k,v)
end
print("\n")

print(tab11==tab22)

输出结果:

table: 00000290A537E1E0
1       122
2       244
3       366
4       44
5       444


table: 00000290A537D9A0
table: 00000290A537D820
table: 00000290A537D720
1       china is
2       best of
3       country 2
4       1


false
PS D:\lua代码> 

24、元表和元方法(二)

--[[

    元表 metatable

        __index
            当在一个表中,去取某个key
                如果该表中有对应的key,就直接返回
                如果没有
                    看有没有元表
                        没有元表,返回nil
                        有元表
                            看元表中有没有__index
                                没有,返回nil
                                有
                                    如果__index 是个表,
                                        并且有key, 返回值
                                            没有key, 返回nil

                                     如果 __index 是个function
                                        则直接调用该function ,且表和key 都会作为该function 的参数

        __newindex
            如果是表,则在本表里面设一个没有的key的时候,会写到__newindex 对应的表中,而不会写自己的表
                     如果本表中有key,则更新本表,不会管元表

            如果是function,则直接调用,且本表,key,value 都可作参数
]]--
local tab1={
    ["id"]=123,
    ["country"]="china",
    ["province"]="shanghai",
    --["phone"]="new_phone"
}

local meta={
    --__index={}                          --—__index是个空表
    --__index={phone="index_phone"}       ---__index是一个表
    __index=function(t,k)
        --print(t,k)
        t[k]="new_phone"
        return "index_phone"
    end
}

-- mm.__index={
--     id=123,
--     ["name"]="tom"
-- }
-- setmetatable(tab1,mm.__index)

print(tab1.phone)           --没有key,直接返回nil
setmetatable(tab1,meta)     --设置meta为tab1的元表,但没有index表返回nil,有则查找index表
print(tab1.phone)           --元表中有key,返回value
tab1.phone=nil              --把自己表中的key去掉
print(tab1.phone)           --自己tab1中有key,直接返回value

local tab2={}
meta.__index=tab2
print(tab1.phone)           --new_phone
print("\n")

local tab3={
    ["id"]=123,
    ["country"]="china",
    ["province"]="shanghai",
    --["phone"]="new_phone"
}
local tab4={}

local meta2={
    --__newindex=tab4
    __newindex=function(t,k,v)
        --print(t,k,v)
        print(tostring(t).." written to "..k..":"..v)
        --t[k]=v         --C stack overflow 死递归,爆栈了
        rawset(t,k,v)    --调用原生方法,不调用重载的方法
    end
}
print(tab3.phone)
setmetatable(tab3,meta2)
tab3["phone"]="new_phone"  --当有__newindex 的时候,就写到对应的__newindex 的表中去了
print(tab3.phone)
print(tab4.phone)

meta2.__index=tab4
print(tab3.phone)

输出结果:

nil
index_phone
index_phone
new_phone


nil
table: 000002446D03BD00 written to phone:new_phone
new_phone
nil
new_phone
PS D:\lua代码> 

25、元表和元方法(三)

--[[

    元表 metatable

        __tostring  用函数接管本表的返回值 返回一个string
        __call      把表当类处理,此处类似于类中的构造函数,可传值,本表是第一个参数

        rawget(table, index)  取本表中的index 索引对应的值,不受元表干扰,没有就返回nil;
        rawset(table, index, value)  给本表添加元素,不受元表干扰


        ---
    ---在不触发任何元方法的情况下 获取 `table[index]` 的值。 `table` 必须是一张表; `index` 可以是任何值。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-rawget"])
    ---
    ---@param table table
    ---@param index any
    ---@return any
    ---@nodiscard
    function rawget(table, index) end


    ---
    ---在不触发任何元方法的情况下 将 `table[index]` 设为 `value。` `table` 必须是一张表, `index` 可以是 `nil` 与 `NaN` 之外的任何值。 `value` 可以是任何 Lua 值。
    ---这个函数返回 `table`。
    ---
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-rawset"])
    ---
    ---@param table table
    ---@param index any
    ---@param value any
    ---@return table
    function rawset(table, index, value) end
]]--
local tab1={
    ["id"]=123,
    ["name"]="tom"
}

local meta={}
setmetatable(tab1,meta)

print(tab1)
print(tab1.phone)
print("\n")

meta.__tostring=function(t)
    local str=""
    for k,v in pairs(t) do
        str=str..k.." : "..v
    end
    t["phone"]=123456
    return str
end

print(tab1)
print(tab1.phone) --在上一句执行完之后,才有phone 字段
print("\n")

meta.__call=function(t,...)
    print(t)
    print(...)
    local t2={}
    for k,v in pairs(t2) do
        print(k,v)
    end
end

tab1(123)
print("\n")
tab1()
print("\n")

local tab2={
    ["id"]=123,
    ["name"]="tom"
}
local tab3={}

print(tab2.id)
print(tab2.phone)
print("\n")

local MT={
    --__index={phone=123456}
    __index=function(t,k)
        print(t,k)
    end,

    __newindex=tab3
}
setmetatable(tab2,MT)
print(tab2.id)
print(tab2.phone)
print("\n")
print(rawget(tab2,"id"))
print(rawget(tab2,"phone"))
print("\n")
print("\n")

print(tab2.country)
--tab2.country="china"
rawset(tab2,"country","china")
print(tab2.country,tab3.country)

输出结果:

table: 000001DCDA5A0770
nil


name : tomid : 123
123456


name : tomphone : 123456id : 123
123


name : tomphone : 123456id : 123



123
nil


123
table: 000001DCDA5A15F0 phone
nil


123
nil




table: 000001DCDA5A15F0 country
nil
china   nil
PS D:\lua代码> 

26、面向对象 self

--[[

    类 class
    对象 new class(实例化)
    继承、构造、析构

    table={}
    metatable={}
    self
]]--

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TeV4I1WZ-1665330518838)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221005183655058.png)]

tab1={
    ["id"]=123,
    ["name"]="tom",
    ["getId"]=function()
        return tab1.id
    end
}
print(tab1.id,tab1.name,tab1.getId())
print("\n")

local tab2=tab1
print(tab1,tab2)
tab1=nil
print(tab2.id,tab2.name)
print(tab2.getId())

输出结果:

123     tom     123


table: 0000022D822910F0 table: 0000022D822910F0
123     tom
C:\Users\hp\.vscode\extensions\actboy168.lua-debug-1.60.0-win32-x64\runtime\win32-x64\lua54\lua.exe: ...于面向对象/lua关于面向对象_self/lua关于面 
向对象_self.lua:17: attempt to index a nil value (global 'tab1')
stack traceback:
        ...于面向对象/lua关于面向对象_self/lua关于面向对象_self.lua:17: in field 'getId'
        ...于面向对象/lua关于面向对象_self/lua关于面向对象_self.lua:27: in main chunk
        [C]: in ?
PS D:\lua代码> 

此时print(tab2.getId())发生错误,错误结果如上图所知,tab1=nil之后,tab2就不能找到tab1.id了。

解决办法1:把对象当做参数传过去

tab1={
    ["id"]=123,
    ["name"]="tom",
    ["getId"]=function()
        return tab1.id
    end,
    ["getName"]=function(obj)
        return obj.name
    end
}
--print(tab2.getId())
print(tab2.getName(tab2))

输出结果:

tom

解决办法2:引入像this指针一样的self引用

tab1={
    ["id"]=123,
    ["name"]="tom",
    ["getId"]=function()
        return tab1.id
    end,
    ["getName"]=function(obj)
        return obj.name
    end
}

function tab1:getId2()
    return self.id
end
function tab1:getName2()
    return self.name
end

print(tab1.id,tab1.name,tab1.getId())
print("\n")

local tab2=tab1
print(tab1,tab2)
tab1=nil
print(tab2.id,tab2.name)
--print(tab2.getId())
print(tab2.getName(tab2))
print("\n")

print(tab2:getId2(),tab2:getName2())

输出结果:

123     tom     123


table: 0000026284C41610 table: 0000026284C41610
123     tom
tom


123     tom
PS D:\lua代码> 

27、面向对象自索引

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bPsUuOup-1665330518838)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221005191539388.png)]

--[[

    自索引  自己索引自己

]]--

local tab1={
    ["id"]=123,
    ["name"]="tom"
}

local indexTable={
    ["phone"]=123456
}

local meta={
    --__index=indexTable
    __index=meta,        --此处meta不存在,所以索引__index=nil
    ["phone"]=123456
}

--meta.__index=meta        --自己索引自己,这就是自索引
setmetatable(tab1,meta)

print(tab1.phone)
print("\n")

print(tab1,indexTable,meta)

输出结果:

nil


table: 000001EA7DEE1720 table: 000001EA7DEE1AA0 table: 000001EA7DEE1760
PS D:\lua代码> 

28、面向对象自索引实现继承

--[[

    自索引:  自己索引自己

    用自索引实现继承

    典型的面向对象写法:子类继承父类,子类调用父类方法
]]
local father={
    ["id"]=123,
    ["name"]="father"
}

function father:fasy()
    print("father say",self.id,self.name)
end

father.__index=father   --父类加自索引

local son={
    ["id"]=456,
    ["name"]="son"
}
function son:sonsy()
    print("son say")
end
setmetatable(son,father)   --设置子类的元表为父类

father:fasy()
son:sonsy()
print("\n")

son:fasy()

输出结果:

father say      123     father
son say


father say      456     son
PS D:\lua代码> 

29、面向对象类的实例化

--[[

    类 class
    对象   new class()
    继承,构造,析构

    table {}
    metatable {}

    class x{
        ....
    }
]]--

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6AWXUBTq-1665330518840)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221005195039237.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZxPgmqge-1665330518840)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221005201428470.png)]

local userinfo={
    ["id"]=123,
    ["name"]="tom",
    ["age"]=23
}
userinfo.__index=userinfo  --自索引

local u1=userinfo
local u2=u1
print(userinfo.id,u1.id,u2.id)
u2.id=456
print(userinfo,u1,u2)
print(userinfo.id,u1.id,u2.id)
print("\n")
function userinfo:new(obj)
    -- local obj=obj or {}
    obj=obj or {}
    setmetatable(obj,self)  --元表继承
    return obj
end
function userinfo:setAeg(val)
    self.age=self.age+val
    return self.age
end

-- local u3=userinfo:new({["id"]=789,["name"]="jerry"})
local u3=userinfo:new({["id"]=789})
userinfo.name="jerry"
print(u3)
print(u2.id,u3.id)
print("\n")
print(u2.name,u3.name)
u3.name="tom"          --__newindex
print(u2.name,u3.name)
userinfo.name="lucy"
print(u2.name,u3.name)
print("\n")

print(u2.age,u3.age)
u3:setAeg(100)
print(u2.age,u3.age)

输出结果:

123     123     123
table: 0000016CCBEB43F0 table: 0000016CCBEB43F0 table: 0000016CCBEB43F0
456     456     456


table: 0000016CCBEB4370
456     789


jerry   jerry
jerry   tom
lucy    tom


23      23
23      123
PS D:\lua代码> 

30、面向对象多重继承

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n9LV61Kl-1665330518841)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221005203241755.png)]

--[[

    --多重继承
]]

local grandFather={
    ["id"]=123,
    ["name"]="grandFather"
}

grandFather.__index=grandFather

local father={
    --["id"]=456,
    ["name"]="father"
}
father.__index=father
setmetatable(father,grandFather)

local son={
    --["id"]=789,
    ["name"]="son"
}
setmetatable(son,father)

print(grandFather.id);--找自己有没有 id 这个索引,有,直接发返回123

print(father.id);--先找自己,没有 id 这个索引,所以看有没有元表,元表是 grandfather,然后去找 grandfather的 __index,找到,所以返回123

print(son.id);--先找自己,没有 id 这个索引,所以看有没有元表,元表是 father,然后去找 father的 __index,
                -- father.__index 里面也没有 id 这个索引,但是 father.__index 有元表,是 grandfather
                -- 所以去找 grandfather.__index 里面有没有 id 这个索引,有,所以返回123

print(grandFather.name,father.name,son.name);
print("\n")

local userInfo={
    ["id"]=456,
    ["name"]="tom",
    ["age"]=23
}

function userInfo:new(obj)
    obj=obj or {}

    self.__index=self        --父类自己索引自己

    setmetatable(obj,self)  ---谁调用new 方法,此处的 元表self 就是谁
    return obj
end

local u1=userInfo:new({["id"]=456})
print(userInfo.id,u1.id)
print(userInfo.name,u1.name)
print("\n")

local u2=u1:new()
print(userInfo.id,u1.id,u2.id)
print(userInfo.name,u1.name,u2.name)
print("\n")

local u3=u2:new({["age"]=22})

print(userInfo.id,u1.id,u2.id,u3.id)
print(userInfo.name,u1.name,u2.name,u3.name)
print(userInfo.age,u1.age,u2.age,u3.age)

输出结果:

123
123
123
grandFather     father  son


456     456
tom     tom


456     456     456
tom     tom     tom


456     456     456     456
tom     tom     tom     tom
23      23      23      22
PS D:\lua代码> 

31、面向对象重写

--[[

    重写
]]

local userInfo={
    ["id"]=123,
    ["name"]="tom"
}

function userInfo:new(obj)
    obj=obj or {}
    setmetatable(obj,self)  ---谁调用new 方法,此处的 元表self 就是谁
    self.__index=self       ---父类的索引
    return obj
end

function userInfo:say()
    print("useinfo say")
end

local u1=userInfo:new()
local u2=u1:new({["id"]=456})
print(userInfo.id,u1.id,u2.id)
print(userInfo.name,u1.name,u2.name)
print("\n")

u1["phone"]=12312
print(userInfo.phone,u1.phone,u2.phone)
u1.phone=nil
print(userInfo.phone,u1.phone,u2.phone)
print("\n")

userInfo:say()
u1:say()
u2:say()
print("\n")

function u1:say()
    print("u1 say")
end
u1:say()
print("\n")

function u2:hello()
    print("u2 hello")
end
--userInfo:hello()
--u1:hello()
u2:hello()

输出结果:

123     123     456
tom     tom     tom


nil     12312   12312
nil     nil     nil


useinfo say
useinfo say
useinfo say


u1 say


u2 hello
PS D:\lua代码> 

32、面向对象成员私有化

--[[

    成员私有化
]]

function userInfo()
    local member={
        ["id"]=123,
        ["name"]="tom",
        ["province"]="shanghai"
    }
    local function getId()
        return member.id
    end
    local function setId(val)
        member.id=val
    end

    --只暴露方法给外面,表中成员变量私有化
    return {
        getId=getId,   --左边key,右边value
        setId=setId,

        getName=function()
            return member.name
        end
    }
end

print(userInfo().getName())
print(userInfo().getId())
print("\n")

local users=userInfo()
print(users.getId())
print(users.getName())
print(users.setId(456))
print(users.getId())

输出结果:

tom
123


123
tom

456
PS D:\lua代码> 

33、协程(coroutine)

--[[

        协程

        协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。
        一个程序可以包含多个协程,可以对比与一个进程包含多个线程,因而下面我们来比较协程和线程。
        我们知道多个线程相对独立,有自己的上下文,切换受系统控制;
        而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程由当前协程来控制。

        【可以把协程看成是一个可以自己自行决定调度切换的线程】

        编程语言Lua从5.0版开始支持协程的概念,极大的扩展了Lua的能力。Lua的协程通过扩展库coroutine来实现

        百度百科  https://baike.baidu.com/item/协程/8652240


        API  https://www.lua.org/manual/5.4/manual.html#6.2

        这里co是协程句柄,由coroutine.create()创建。

        coroutine.close (co) -------------------- 关闭协程,返回bool
        coroutine.create (f) -------------------- 创建协程,传入一个function,返回一个协程句柄
        coroutine.isyieldable ([co]) ------------ 判断协程是否是 yield 状态
        coroutine.resume (co [, val1, ···]) ----- 将挂起态的协程重新激活
        coroutine.running () -------------------- 获取正在运行的协程
        coroutine.status (co) ------------------- 获取co句柄对应的协程的状态 [suspended(挂起),running(执行中),dead(结束)]
        coroutine.wrap (f) ---------------------- 用function 创建一个新的协程
        coroutine.yield (···) ------------------- 挂起当前协程


        对于线程
        线程(英语:thread)是操作系统能够进行运算调度的最小单位。
        它被包含在进程之中,是进程中的实际运作单位。
        一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
        在Unix System V及SunOS中也被称为轻量进程(lightweight processes),
        但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

        线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,
        如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如		  Windows 7的线程,进行混合调度。

        同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。
        但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己		的线程本地存储(thread-local storage)。
        一个进程可以有很多线程,每条线程并行执行不同的任务。

        在多核或多CPU,或支持Hyper-threading的CPU上使用多线程程序设计的好处是显而易见,即提高了程序的执		  行吞吐率。
        在单CPU单核的计算机上,使用多线程技术,也可以把进程中负责I/O处理、人机交互而常被阻塞的部分与密集计 		 算的部分分开来执行,
        编写专门的workhorse线程执行密集计算,从而提高了程序的执行效率。
        https://baike.baidu.com/item/%E7%BA%BF%E7%A8%8B/103101?fromModule=lemma-		 qiyi_sense-lemma


        对于进程
        进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,		 是操作系统结构的基础。
        在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;
        在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的		  实体。
        https://baike.baidu.com/item/%E8%BF%9B%E7%A8%8B/382503?fromModule=lemma-qiyi_sense-lemma
]]--

协程主要函数如下:

---@meta

        ---
        ---
        ---
        ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-coroutine"])
        ---
        ---@class coroutinelib
        coroutine = {}

        ---
        ---创建一个主体函数为 `f` 的新协程。 f 必须是一个 Lua 的函数。 返回这个新协程,它是一个类型为 `"thread"` 的对象。
        ---
        ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-coroutine.create"])
        ---
        ---@param f async fun()
        ---@return thread
        ---@nodiscard
        function coroutine.create(f) end

        ---
        ---如果协程 `co` 可以让出,则返回真。`co` 默认为正在运行的协程。
        ---
        ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-coroutine.isyieldable"])
        ---
        ---@param co? thread
        ---@return boolean
        ---@nodiscard
        function coroutine.isyieldable(co) end

        ---@version >5.4
        ---
        ---关闭协程 `co`,并关闭它所有等待 *to-be-closed* 的变量,并将协程状态设为 `dead` 。
        ---
        ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-coroutine.close"])
        ---
        ---@param co thread
        ---@return boolean noerror
        ---@return any errorobject
        function coroutine.close(co) end

        ---
        ---开始或继续协程 `co` 的运行。
        ---
        ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-coroutine.resume"])
        ---
        ---@param co    thread
        ---@param val1? any
        ---@return boolean success
        ---@return any ...
        function coroutine.resume(co, val1, ...) end

        ---
        ---返回当前正在运行的协程加一个布尔量。 如果当前运行的协程是主线程,其为真。
        ---
        ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-coroutine.running"])
        ---
        ---@return thread running
        ---@return boolean ismain
        ---@nodiscard
        function coroutine.running() end

        ---
        ---以字符串形式返回协程 `co` 的状态。
        ---
        ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-coroutine.status"])
        ---
        ---@param co thread
        ---@return
        ---| '"running"'   # 正在运行。
        ---| '"suspended"' # 挂起或是还没有开始运行。
        ---| '"normal"'    # 是活动的,但并不在运行。
        ---| '"dead"'      # 运行完主体函数或因错误停止。
        ---@nodiscard
        function coroutine.status(co) end

        ---
        ---创建一个主体函数为 `f` 的新协程。 f 必须是一个 Lua 的函数。 返回一个函数, 每次调用该函数都会延续该协程。
        ---
        ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-coroutine.wrap"])
        ---
        ---@param f async fun()
        ---@return fun(...):...
        ---@nodiscard
        function coroutine.wrap(f) end

        ---
        ---挂起正在调用的协程的执行。
        ---
        ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-coroutine.yield"])
        ---
        ---@async
        ---@return any ...
        function coroutine.yield(...) end

        return coroutine
--业务,取钱,取100w
local function func1(a,b)

    local aa=a*2
    local bb=b*2
    local x,y,z=coroutine.yield(aa,bb,"100w")
    print(x,y,z)

    print(coroutine.status(handle)) --这里能看出协程是执行中
    print("\n")
end

handle=coroutine.create(func1) --- 去银行办业务, 然后告诉大堂经理,要取现金(要执行func1),大堂经理会帮你叫号,给你一个小票(handle)
print(handle)
print(type(handle))
print("\n")

print(coroutine.status(handle))      --- 挂起状态,等待执行或结束,(你拿到小票了,就去等着叫号,下一步,也可能这个号作废,也可能叫你去办业务)
print("\n")

--print(coroutine.resume(handle))      --- 叫到你的号了,你拿着小票去窗口办业务(执行 func1)
print(coroutine.resume(handle,11,22))  --- resume是阻塞的,要等到调用结束,然后返回调用成功或失败状态(bool)以及其它值
                                       ----这里的coroutine.resume执行的是local x,y,z=coroutine.yield(aa,bb,"100w")等号左边部分
                                       --- 并且把你的身份证,银行卡还给你,再把你取的一百万给你
print(coroutine.status(handle))
print("\n")

print(coroutine.resume(handle,"aa","bb","cc")) --再办一个业务,查查我其它的卡上的余额,IC卡,IQ都给你(aa,bb,cc)
                                              --这里的coroutine.resume执行的是local x,y,z=coroutine.yield(aa,bb,"100w")等号右边部分
print(coroutine.status(handle))
print("\n")

coroutine.close(handle)                ---- 业务办完了,好评结束
print(coroutine.status(handle))        ---- dead 结束状态
print("\n")

输出结果:

thread: 00000230356A9418
thread


suspended


true    22      44      100w
suspended


aa      bb      cc
running


true
dead


dead


PS D:\lua代码> 

34、协程(coroutine)双循环切换

普通方法双循环输出:

--[[

    --协程(coroutine)双循环切换
]]

local function sleep(n)
    local now=os.clock()
    while now+n >=os.clock() do
        -- statements
    end
end

local function func1()
    while true do
        print("func 11")
        sleep(1)
        print("func 22")
        sleep(1)
    end
end

local function func2()
    while true do
        print("func 22")
        sleep(1)
    end
end

func1()
--func2()

输出结果:

func 11
func 22
func 11
func 22
func 11
func 22
func 11
func 22
func 11
func 22
func 11
func 22
func 11
func 22
func 11
func 22
func 11

定义一个标志位输出双循环切换:

local function func3(flag)
    while true do
        print("func 33")
        sleep(1)
        if flag%2 ==0 then
            break
        end
    end
end

local function func4(flag)
    while true do
        print("func 44")
        sleep(1)
        if flag %2 ~=0 then
            break
        end
    end
end

local flag=0
while true do
    if flag %2 ==0 then
        func3(flag)
    else
        func4(flag)
    end
    flag=flag+1
end

输出结果:

func 11
func 22
func 11
func 22
func 11
func 22
func 11
func 22
func 11
func 22
func 11
func 22
func 11
func 22
func 11
func 22
func 11

协程实现双循环切换:

local function func5()
    while true do
        local rs=coroutine.resume(handle2)
        print(rs,"func 55")
        sleep(1)
    end
end

local function func6()
    while true do
        print("xxxsxxxxxxxxxx")
        coroutine.yield()
        print("func 66")
        sleep(1)
    end
end

local handle1=coroutine.create(func5)
handle2=coroutine.create(func6)
coroutine.resume(handle1)

输出结果:

xxxsxxxxxxxxxx
true    func 55
func 66
xxxsxxxxxxxxxx
true    func 55
func 66
xxxsxxxxxxxxxx
true    func 55
func 66
xxxsxxxxxxxxxx
true    func 55
PS D:\lua代码> ^C

记住一点:调用者与被调用者之间的resume-yield关系不会颠倒。当一个协同程序调用yield时不会进入新的函数也就是不会执行yield之下的代码,而是执行的是一个未决的resume,意思就是返回执行resume左边的代码。同样,当一个协同程序调用resume时不会进入新的函数也就是不会执行resume之下的代码,而是返回yield的调用。

35、协程(coroutine)生产者消费者模型


--[[

    协程

    生产者消费者模型

    你去吃串,
        生产者  烤串的小哥
        消费者  你

    小哥烤一串,你吃一串
    小哥烤一串,你吃一串
    小哥烤一串,你吃一串
    ....

    这就是生产者消费者模型

]]--
local function sleep(n)
    local now=os.clock()
    while now+n >=os.clock() do
        -- statements
    end
end
local function prod()
    local i=0
    while true do
        i=i+1
        coroutine.yield(i)  --生产者生产出后,返回给消费者,并挂起,
    end
end

local function consume()
    while true do
        local flag,i=coroutine.resume(prodHandle)   --消费者用协程取找生产者要
        print(flag,i)
        sleep(2)
    end
end

prodHandle=coroutine.create(prod)
local consumHandle=coroutine.create(consume)
coroutine.resume(consumHandle)                       --从消费者开始

输出结果:

true    1
true    2
true    3
true    4
true    5
true    6
true    7
true    8
true    9
true    10
true    11
true    12
true    13
true    14
true    15
true    16
true    17
true    18
true    19
true    20
true    21
true    22
true    23

36、文件操作

--[[

    文件操作

    io.open (filename [, mode])

        r	只读方式打开,文件必须存在。
        w	只写方式打开,文件存在则删除原有内容,文件不存在则创建。
        a	追加方式打开,文件存在,则写入的数据会往最后追加,文件不存在则创建。
        r+	读写方式打开,文件必须存在。
        w+	读写方式打开,文件存在则删除原有内容,文件不存在则创建。
        a+	与a类似,但文件可读写。
        b	二进制模式,如果文件是二进制文件,可以加上b
        +	修饰符,表示对文件既可以读也可以写


    io.read()
        *n  读取一个数字 读取不为字符的后续数字,遇到字符就停止读取
        *a  读取所有内容
        *l  默认值,读取下一行
        123 从当前位置开始,读取123个字符长度

    file:seek(whence, offset)

    whence
        set 从头开始
        cur 从当前位置开始
        end 从末尾开始


    file:seek() 无参数,返回的是当前位置



    ---
    ---用字符串 `mode` 指定的模式打开一个文件。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-io.open"])
    ---
    ---@param filename string
    ---@param mode?    openmode
    ---@return file*?
    ---@return string? errmsg
    ---@nodiscard
    function io.open(filename, mode) end

    ---
    ---设置 `file` 为默认输入文件。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-io.input"])
    ---
    ---@overload fun():file*
    ---@param file string|file*
    function io.input(file) end


    ---读文件 `file`, 指定的格式决定了要读什么。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-io.read"])
    ---
    ---@param ... readmode
    ---@return any
    ---@return any ...
    ---@nodiscard
    function io.read(...) end

    function io.read(...integer|"L"|"a"|"l"|"n")
    读文件 file, 指定的格式决定了要读什么。

    查看文档

    ...(param):
        | "n" -- 读取一个数字,根据 Lua 的转换文法返回浮点数或整数。
        | "a" -- 从当前位置开始读取整个文件。
       -> "l" -- 读取一行并忽略行结束标记。
        | "L" -- 读取一行并保留行结束标记。
    end


    ---设置 `file` 为默认输出文件。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-io.output"])
    ---
    ---@overload fun():file*
    ---@param file string|file*
    function io.output(file) end

    ---
    ---设置及获取基于文件开头处计算出的位置。
    ---
    ---[查看文档](command:extension.lua.doc?["en-us/54/manual.html/pdf-file"])
    ---
    ---@param whence? seekwhence
    ---@param offset? integer
    ---@return integer offset
    ---@return string? errmsg
    function file:seek(whence, offset) end
]]--
local f1=io.open("file/a.txt","r+")
if f1==nil then
    print("文件不存在")
    os.exit()
end
local f2=io.open("file/b.txt","w+")
if f2==nil then
    print("文件不存在")
    os.exit()
end
-- function io.read(...integer|"L"|"a"|"l"|"n")
--     读文件 file, 指定的格式决定了要读什么。

--     查看文档

--     ...(param):
--         | "n" -- 读取一个数字,根据 Lua 的转换文法返回浮点数或整数。
--         | "a" -- 从当前位置开始读取整个文件。
--        -> "l" -- 读取一行并忽略行结束标记。
--         | "L" -- 读取一行并保留行结束标记。
-- end

io.input(f1)                    --把f1的文件句柄放入到io,这里指的是当前输入终端
--print(io.read("*a"))          --*a是读取文件中所有内容
print(io.read("*l"))            --*l是读取一行并忽略结束标记
--print(io.read("*L"))          --"L" -- 读取一行并保留行结束标记。
--print(io.read("*L"))
print(io.read("*n"))           --*n  读取一个数字 读取不为字符的后续数字,遇到字符就停止读取
print("\n")

while true do
    local str=io.read()
    if str== nil then
        break
    end
    print(str)
end
print("\n")

io.write("AB")
io.output(f1)   --把io的内容写到 f1
--io.write(1234567890)
io.close(f1)
--f1:close()
print("\n")


io.output(f2)       --把io的内容输出到 f2文件中
io.write("abc")
io.write(123)
--io.input(f2)
--print(io.read("*a"))
print("\n")
io.close(f2)

local f3=io.open("file/a.txt","r+")
if f3 == nil then
    print("文件不存在")
    os.exit()
end

io.input(f3)
print(f3:read("*l"))
print(f3:read("*a"))
f3:close()
print("\n")

local f4=io.open("file/a.txt","r+")
if f4 == nil then
    print("文件不存在")
    os.exit()
end
print("\n")

print(f4:seek())
io.input(f4)
print(io.read("*l"))
print(f4:seek())
print("\n")

print(f4:seek("cur"))
print(io.read("*l"))
print("\n")

print(f4:seek("set"))
print(io.read("*l"))
print("\n")

print(f4:seek("end",-5))
print(io.read("l"))

输出结果:

123456
654


a321
abcdef


AB



123456
654a321
abcdef




0
123456
8


8
654a321


0
123456


18
bcdef
PS D:\lua代码> 

37、包(模块)管理

local helper2={}

helper2.getIp=function ()
    return "127.0.0.1"
end

helper2.getVersion=function()
    return "1.0"
end

local tools={}

tools.getOs=function()
    return "windows"
end

-- return {helper=helper,tools=tools}
return helper2
local helper1={}

helper1.getIp=function ()
    return "127.0.0.1"
end

helper1.getVersion=function()
    return "1.0"
end

local tools={}

tools.getOs=function()
    return "windows"
end

-- return {helper=helper,tools=tools}
return helper1
local tmpHelper=require("helper")
local tmpHelper2=require("file/helper2")   --相对路径


package.path=package.path..";/tmp/?.lua"   --把tmp文件下的lua文件加入package.path绝对路径


print(require("helper"))


helper={
    getIp=function()
        return "192.168.1.1"
    end
}
print("\n")
print(helper.getIp())
print(tmpHelper.getIp())
print(tmpHelper.getVersion())
print(tmpHelper2.getVersion())
--print(tmpTools.getOs())
print("\n")

--package.preload
--package.loaded;

--找lua源文件和so 库
print(package.path)
print(package.cpath)
print("\n")

for k,v in pairs(package.preload) do
    print(k,v)
end
print("\n")

for k,v in pairs(package.loaded) do
    print(k,v)
end

输出结果:

table: 0000021704467FF0


192.168.1.1
127.0.0.1
1.0
1.0


D:\lua代码/?.lua;D:\lua代码/?.lua;/tmp/?.lua
D:\lua代码/?.dll;D:\lua代码/?.dll




package table: 0000021702A73A80
io      table: 0000021702A81330
table   table: 0000021702A814F0
string  table: 0000021702A813B0
os      table: 0000021702A81370
_G      table: 0000021702A74100
helper  table: 0000021704467FF0
utf8    table: 0000021702A81970
math    table: 0000021702A81670
coroutine       table: 0000021702A816F0
debug   table: 0000021702A81530
file/helper2    table: 0000021704468270
PS D:\lua代码> 

38、操作mysql数据库

--[[

    luasql  https://luarocks.org/

    brew install luarocks
    luarocks install luasql-mysql   注意此处,如果你是 mariadb,然后要求指定 MYSQL_DIR 参数的时候,
                                        千万不要指到 mariadb 的安装目录,而是要指到一个真正的mysql 安将目录
                                        那是不是不能用 mariadb 呢,也不是,要手动安装一遍

    luarocks install luasql-sqlite3
    luarocks install luasql-postgres
    luarocks install luasql-mysql
    luarocks install luasql-sqlite
    luarocks install luasql-odbc

]]--

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-60iJjWmf-1665330518842)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221006222447305.png)]

local luasql = require("luasql.mysql");

client = luasql.mysql();

--创建连接
conn=client:connect("lua-test","root","12345678","127.0.0.1",3306); --数据库名,用户名,密码,IP,端口

if conn == nil then
    print("连接失败");
    os.exit();
end

--select
rs=conn:execute("select * from user_list order by id desc");

row = rs:fetch({},"a");

while row do

    print(row.id,row.name,row.age);

    row = rs:fetch(row,"a");

end

--insert
--rs=conn:execute("insert into user_list (name,age)values('def',456)");
--print(rs);

--update
rs=conn:execute("update user_list set name='DEF',age=789 where id<=4");
print(rs);

--delete
rs=conn:execute("delete from  user_list where id=4");
print(rs);

conn:close();
client:close();

39、操作redis缓存

--[[

    luarocks install luasocket         module 'socket' not found

    https://github.com/nrk/redis-lua

    最历害的是,用redis 去跑lua,分布式锁,限流,




]]--
local redis = require("redis");


local config={host="127.0.0.1",port=6379};
local client = redis.connect(config);


--原子操作,秒杀,限流,热更新
--eval "local val=redis.call('GET',KEYS[1]);if _G.tonumber(val)>0 then redis.call('DECR',KEYS[1]) end;return true" 1 test-key


local val=client.get('test-key');--1
if _G.tonumber(val) <1 then

    print("没有库存,抢完了");
    os.exit();
else
    client.decr('test-key');
end




--[[
info = client:info();

for k,v in pairs(info.clients) do
    print(k,v);
 end
 ]]--

print(client:get("test-key"));
--print(cliREADMEent:del("test-key"));

--print(client:set("test-key",456));

print(client:incr("test-key"));

print(client:get("test-key"));

for k,v in pairs(redis.commands) do
    print(k,v);
end


40、全局环境变量和环境隔离

--[[

    在lua 标准库中,所有的内容,都有一个隐式的前缀 _G.
    _G 就是lua 中的全局环境变量,就是lua 的老祖宗,就是开天辟地的盘古
    _G 生而不local,一改就影响全局

    local _ENV 指定局部环境变量

]]
--[[
function func1()
    _G["os"]=nil;
    print(os);
end
]]--



--沙箱-沙盒-虚拟机-虚拟环境
--可以指定特有的环境变量
function func2(code)
    local rs,msg = load(code,"前端上传的代码有问题","bt",{print=_G.print});--预处理 的时候,环境变量中,只有一个print

    if rs == nil then
        print(msg);
        os.exit();
    end

    return pcall(rs);
end


function func3()
    local _ENV={print=print};-- setfenv(1,{os})
    print(os);
end


--print(os);
--func3();
--print(os);


--[[
print(_G.type);
print(os);

func1();
print(os);
]]--


--假设这个字符串是前端传过来的,现在要求你去当作代码执行

--str="print(123);os.remove('file/a.txt');print(os)";--这个代码是危险代码,因为会删文件
str="print(123);print(os)";

func2(str);--咋们肯定不能让它删文件

Lua与c与c++的交互编译。

Lua在高并发的场景中的使用-分布式锁。

Lua怎么去处理游戏的热更新。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到