• Siki_Unity_3-8_Lua编程(未完)


    Unity 3-8 Lua编程

    任务1&2&3:前言

    课程内容:
      Lua从入门到掌握
      为之后的xLua和其他热更新方案打下基础

    任务4:Lua简介

    Lua是轻量小巧的脚本语言--无需编译,用标准c语言编写,开源
      目的是为了嵌入应用程序中,为应用程序提供灵活的扩展和定制功能
      可以很方便地与其他程序进行集成(如C++, C#, Java等)
        -- 因为Lua无需编译,对于某些功能,如果不方便重新编写,可以使用Lua进行功能扩展

    Lua应用场景:
      游戏开发
      独立应用脚本
      Web应用脚本
      扩展和数据库插件
      安全系统(如入侵检测系统)

    Lua和c#的区别:
      Lua可以在几乎所有操作系统/平台运行
        可以很方便的更新代码,代码更新后无需重新安装 -- 后续的热更新方案
        不需要c#那样重新编译打包
      c#只能在特定的操作系统中进行编译成dll文件,然后打包进安装包在其他平台运行
        在移动平台上不能更新替换已有的dll文件,除非重新下载安装包

    学习资料:
      Programming in Lua
      www.runoob.com/lua/lua-tutorial.html -- 菜鸟教程
      www.luaer.cn -- Lua中国开发者
      官网:www.lua.org

    任务5&6:Lua的安装(SciTE && LuaDist) 和第一个Lua程序

    http://www.runoob.com/lua/lua-environment.html
    SciTE -- https://github.com/rjpcomputing/luaforwindows/releases,带IDE
    LuaDist -- 无IDE

    任务7:注释

    -- 单行注释

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

    -- 单行注释
    --[[
        多行注释
        小技巧:在多行注释开头再加一个-,会使多行注释代码块生效
        原因是第一行和最后一行都变成了单行注释
    ]]--
    
    ---[[
        print("Execute")
    --]]

    任务8:标识符命名规则和关键字

    标识符:字母或_开头,后跟0或多个字母或_或数字

    不推荐"_跟大写字母作为标识符",比如"_NAME",这是lua的保留字命名规则(Lua内部使用的变量名)

    标识符区分大小写

    任务9、21:全局变量和局部变量

    默认情况下的变量都是全局的
    全局变量不需要声明,给一个变量赋值后就相当于创建了这个全局变量,比如 a = 10
      访问一个没有赋值的全局变量,此时不会报错,但是变量不存在,比如 print(b) 的结果为 nil
      而如果需要删除一个全局变量,赋值为nil即可,如 a = nil,删除后则a不存在

    局部变量的声明:local b = 10
      局部变量的生命周期为:从声明开始,到所在语句块结束
      如果是语句块是在最外围,则整个lua脚本文件的语句执行完后才被销毁

    function f () 
        a = 1
        local b = 2
        -- a为全局变量,b为局部变量
    end
    
    f()      -- 注意,如果这边没有调用函数f(),则上面函数定义中的变量是没有被创建的
    
    print(a)    -- 1
    print(b)    -- nil

    函数也是可以通过local来修饰为局部函数的

    局部变量和全局变量的重名规则:
      有局部变量时局部变量优先

    一般而言,访问局部变量的速度会比访问全局变量快,而且局部变量在生命周期结束后会被销毁释放内存空间

    任务10~20:数据类型的简介

    基本类型:

    nil -- 只有一个值nil,表示无效值,条件表达式内相当于false

    boolean -- true / false

    number -- 双精度实浮点数

    string -- "string" 或 'string'

    function -- 函数

    userdata -- 任意存储在变量中的C数据结构

    thread -- 独立执行的线路,协程(非线程)

    table -- 表(关联数组),索引为数字或字符串,{}表示一个空表

    使用type可以获取变量或者值的类型,如

    print(type("hellolua"))    -- string
    print(type(10))    number
    print(type(1.1)    number
    print(type(print))    function
    print(type(type))    function
    print(type(nil))    nil
    print(type(x))    nil

    nil:nil类型只有一个值nil,表示无效值,比如当变量未被赋值时,即为nil值

    给变量赋值nil后,表示将该变量销毁,释放内存

    boolean:nil值视为为false,其他变量值都视为true,比如if(10)是true

    string
      表示方法:"" 或 ‘’ 或 [[ ]] (表示多行)
      "string"
      'string'
      [[str
        in g ]]

      字符串的拼接,不能用 + 号,只能用 .. ,如 print("a".."b") --> "ab"     ;     print(2..6) --> 26
      print("2" + "6") --> 8 会自动转换为number类型
      #表示字符串长度(字节数),如  s = "ab呀"; print(#s) --> 4

    table:

    构造方法及使用

    table1 = { }  -- 构造表达式,得到一个空表
    print(table1)        -- -> table:00A89600
    print(table1.key1)    -- -> nil
    
    table2 = {key1 = 100, key2 = "aaa"}
    print(table2.key1)        -- ->100
    print(table2["key1"])    -- ->100
    
    table3 = {"111", "222", "hello"}
    print(table[2])       -- -> 222 -- Lua中没有数组,用table表示数组。
    -- 在Lua中,索引一般是从1开始算的,不是0
    
    -- table的索引
    for key,val in pairs(table3) do
        print(key..":"..val);
    end
    -- -> 1:111 2:222 3:hello 

    table的大小不是固定的,可以很灵活的进行增删查改

    -- 对于字典形式
    tbl = {key1 = "aaa"}
    -- 修改
    tbl.key1 = 123    -- 类型可以不相同
    
    -- 增加
    tbl["key2"] = "value"
    tbl.key222 = "value"
    tlb[10] = 1000
    
    -- 删除
    tbl.key2 = nil    -- 此时进行遍历就不会遍历到key2了
    
    -- 对于数组形式(实质上还是字典,只不过把自动索引作为了key)
    tbl = {"1", "a"}
    tbl[2] = "b"        -- 修改
    tbl[3] = "c"        -- 增加
    tbl[2] = nil         -- 删除 -- 注意,这里的数组本质上还是键值对的形式
    -- 比如这边把索引为2的删除后,遍历可得到 --> 1:1  3:c (索引不是连续的)
    -- 也同样可以添加键值对
    tbl["key5"] = "value5"
    
    -- 删除整个表
    tbl = nil

    function:函数

    不需定义返回值类型,但是可以返回任意类型返回值

    函数体为 从 function name(argument) 到 end 之间

    function f1(n)
        print(n)
        return n
    end

    可作为一个类型作赋值操作

      f2 = f1 -- 此时f2也为一个function类,函数内容和f1一样

    可以作为参数传递

    function func1(table fun)
        for key, value in pairs(table)
            fun(key, value)
        end
    end
    
    function func2(key, value)
        print(key..":"..value)
    end
    
    func1(table, func2) -- 遍历table并输出key,value

    匿名函数:只能使用一次,不用定义函数名

    -- 以上一段代码为例,调用的时候
    func1(table, 
        function (key, value)
            print(key..":"..value)
        end
    )
    -- 遍历table输出键值对

    thread:协同程序

    不是线程

    拥有自己独立的栈、局部变量和指令指针

    可以共享全局变量和其他大部分东西

    任意时刻,协程只能运行一个,只有被挂起 suspend 的时候协程才会暂停

    userdata:自定义类型

    用于表示由应用程序或C/C++创建的类型,可以将C/C++任意数据类型 (通常为struct和指针) 存储到lua变量中进行调用

    任务22:多变量赋值

    a, b, c = 1, 2, "c"

    a, b = b, a  -- 并不是简单的a = b; b = a;,而是很方便的交换了a和b的值 -- 结果为a = 2, b = 1
      因为lua是将右边的值全部计算完后,再统一赋值的

    a, b = 10, 20, 30 -- 30会被忽略

    a, b, c = 10, 20   -- 未赋值的变量会初始化为默认值,即为nil

    函数function也是可以直接返回多个值的
      function test()
        return 1,2
      end
      a, b = test()

    任务23、24、26:流程控制

    while循环

    while (condition) do
      statements
    end

    condition的括号可加可不加

    for循环

    数值for循环

    for var = start, end, step do
      statements
    end

    var从start到end (inclusive),每次增加step
    step未指定时默认为1

    泛型for循环

    for key, value in pairs (table) do
      statements
    end

    repeat unitl循环(和do while流程相同,逻辑相反):

    repeat
      statements
    until (condition)

    直到满足condition后,就跳出循环(而不是do while的继续循环)

    if判断

    if (condition) then
      statements
    end

     

    -- 注意,数值0表示true,nil值表示false

    if (condition) then
      statements
    elseif (condition) then
      statements
    else
      statements
    end

    任务27、28:function详解

    [local] function name(arg1, arg2, ..., argn)
      statements
      [return value1, value2, ... valuen]
    end

    1. function本质为变量类型,因此可以作为数据直接赋值,也可以作为参数传递

    2. 可直接返回多个值,可用多变量赋值的方式接收返回值(如果接收返回值的变量数不对,则遵循多变量赋值的规则)
      如:func返回值为4个,调用处代码 a, b = func(),则后两个返回值就没有获取到了

    3. 可变参数,传递的参数个数可以是多个

    -- 定义时 
    function func(...)
        print(arg)  
        -- 这边会将多个参数封装成一个table类型,以便使用
        -- table中保存了所有传入的参数,最后一位保存了参数的总个数
        local arg = {...}
        -- 此时args里保存的为所有传入的参数,不带最后一个总个数了
    end
    
    -- 调用时
    func(a)
    func(a, b)

    任务29:运算符

    数学运算符:

    ^  -- 求幂

    % -- 求余(可对小数进行求余)

    关系运算符:

    ~=  -- 不等

    逻辑运算符:

    and  -- 与

    or  -- 或

    not  -- 非

    其他运算符:

    ..  -- 字符串连接

    #  -- 取得字符串长度

    任务32、33:字符串常见操作

    newstring = string.upper(oldstring)
    newstring  = string.lower(oldstring)
    newstring  = string.gsub(oldstring, "o", "r", num)  -- 将str1中的 o 替换成 r, num 表示替换的最大次数,可以不传
    index = string.find(str, "substr", num)  -- 从num开始,搜索str是否有子字符串substr,返回子字符串开始的索引
    newstring = string.reverse(oldstring)
    str = string.format("%02d", month)
    str = string.char(97, 98, 99)  -- 对应ASCII码。abc
    integer = string.byte("ABCD", num)  -- 返回num个字符对应的值。不写num时默认为第一个字符
    newstring  = string.rep(oldstring, count)  -- oldstring重复count次
    string.gmatch()/ string.match() -- 正则表达式

    任务35:多维数组

    array = { {"a", "b"}, { "1", "2"}, {"xx", "yy"} }
    print(array[1][1])  -- 输出a

    任务36、37:迭代器函数

    pairs 遍历 table,表中所有的key和value
    ipairs 按索引遍历,从1开始,递增遍历,遇到nil值就停止

    自定义迭代函数:

    .......

    ......

    ......

    ......

    ......

    .......

    ......

    ......

    ......

    ......

    .......

    ......

    ......

    ......

    ......

    .......

    ......

    ......

    ......

    ......

    任务50~54:协同程序

    Lua中协程和线程的区别有:线程是由CPU进行任务调度的,可以视为同一时间可以同时运行多个线程
      而协程是在同一个线程中的,同一时刻只能在运行一个协程,需要代码控制多协程的协作,对协程进行挂起或继续等操作

    基本用法:

    -- 定义方法1
    co = coroutine.create (
        function (a,b)    -- 这里是一个匿名函数
            print(a)
        end
    )
    
    -- 对应的调用
    coroutine.resume(co, 20, 30)
    
    -- 定义方法2
    co = coroutine.wrap (
        function (a,b)    -- 区别只有调用的函数不同,这边是wrap
            print(a)
        end
    )
    -- wrap对应的调用,直接启动即可,不需通过resume()
    co(20,30)
    
    -- 挂起:在上面定义的匿名函数中,使用coroutine.yield()语句即可挂起协程
    co = coroutine.wrap (
        function (a,b)    -- 区别只有调用的函数不同,这边是wrap
            print(a)
            coroutine.yield()
            print(b)
        end
    )
    
    -- 恢复运行协程 -- 注意:当一个协程执行完毕return后,再进行resume并传参进去,并不能再次开始执行协程
    coroutine.resume(co)
    
    -- 返回值
    -- 假设上面的匿名函数的最后一句为
    return a+b, a-b
    -- 调用/继续的时候接收返回值(如果没有执行到return语句,只会有第一个true/false的返回值表示该次调用/继续是否成功)
    res1, res2, res3 = coroutine.resume(co, 20,30)
    print(res1, res2, res3)
    -- 结果为true, 50, -10,其中第一个返回值表示resume成功与否
    -- 如果想在挂起的时候有返回值的话,在yield中传递参数
    coroutine.yield(a*b)
    -- 调用/继续resume的时候会得到额外的返回值
    res1, res2 = coroutine.resume(co, 20, 30)
    -- res1 = true, res2 = 600

    其他函数:

    coroutine.status(co)
      有三个值:
        running:协程正在执行过程中
        suspended:协程未开始或暂停挂起后
        dead: 协程执行完毕后

    coroutine.running()
      当前正在运行的协程,返回值为协程的地址
      如果当前没有正在运行的协程(包括未开始、挂起都不算正在运行),则输出nil

    加大难度:协同程序内部和外部的数据交流

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

    function foo (a)
        print("foo 函数输出", a)
        return coroutine.yield(2 * a) -- 返回  2*a 的值
    end
     
    co = coroutine.create(function (a , b)
        print("第一次协同程序执行输出", a, b) -- co-body 1 10
        local r = foo(a + 1)
         
        print("第二次协同程序执行输出", r)
        local r, s = coroutine.yield(a + b, a - b)  -- a,b的值为第一次调用协同程序时传入
         
        print("第三次协同程序执行输出", r, s)
        return b, "结束协同程序"                   -- b的值为第二次调用协同程序时传入
    end)
           
    print("main", coroutine.resume(co, 1, 10)) -- true, 4
    print("--分割线----")
    print("main", coroutine.resume(co, "r")) -- true 11 -9  -- 此处resume的参数中,除了第一个参数,剩下的参数将作为yield的参数
    print("---分割线---")
    print("main", coroutine.resume(co, "x", "y")) -- true 10 end
    print("---分割线---")
    print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
    print("---分割线---")
    
    --[[
    第一次协同程序执行输出    1    10
    foo 函数输出    2
    main    true    4
    --分割线----
    第二次协同程序执行输出    r
    main    true    11    -9
    ---分割线---
    第三次协同程序执行输出    x    y
    main    true    10    结束协同程序
    ---分割线---
    main    false    cannot resume dead coroutine
    ---分割线---
    ]]--

    任务55~58:文件操作

    I/O的简单模式:

    file = io.open(filename [, mode])
      mode:
      r -- 只读,文件必须存在
      w -- 只写,若文件已存在则清空内容后开始写入,若文件不存在则创建新文件开始写入
      a -- 附加方式打开只写文件,若文件不存在则创建,若文件存在则从文件尾(保留EOF符)开始写入
      r+ / w+ / a+:文件是可读写文件
      b -- 针对二进制文件,进行二进制模式的打开

    io.flush() -- 向文件中写入缓冲中的所有数据

    -- 读取
    file = io.open("name.txt", "r")
    io.input(file)
    io.read()    -- 读取文件流中的一行数据
    io.read("*l")     --默认值,和io.read()一样,读取下一行,在文件尾EOF处返回nil
    io.read("*n")    -- 读取一个数字
    io.read("*a")    -- 读取文件中的所有内容
    io.read(9)    -- 读取之后的9个字符
    io.close(file)
    
    -- 写入
    file = io.open(..., "a")
    io.output(file)
    io.write("new string")
    io.close(file)

    I/O的完全模式:需要同一时间处理多个文件时

    以file:的方式进行调用(注意是 : ),而不是io.的方式

    file = io.open("xx.txt","a")
    file:read()
    file:write("xxxxx")
    file:close()

    file:seek(optional whence, offset)  -- 把光标移到对应位置后开始操作

    任务59:垃圾回收机制

    Lua采用了自动内存管理机制

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    CNN网络架构演进:从LeNet到DenseNet
    【OCR技术系列之四】基于深度学习的文字识别(3755个汉字)
    【OCR技术系列之三】大批量生成文字训练集
    【Keras】基于SegNet和U-Net的遥感图像语义分割
    Python各类图像库的图片读写方式总结
    我的2017:从工作再到学生
    VS2017 WinFrom打包设置与教程
    采用Opserver来监控你的ASP.NET项目系列(三、监控你的服务器状态)
    采用Opserver来监控你的ASP.NET项目系列(二、监控SQL Server与Asp.Net项目)
    采用Opserver来监控你的ASP.NET项目系列(一、Opserver监控的简介与平台搭建)
  • 原文地址:https://www.cnblogs.com/FudgeBear/p/8857795.html
Copyright © 2020-2023  润新知