• 《Lua程序设计》读书笔记


    ·概要:

        还是在工作之后才听说的Lua脚本语言--游戏制作领域的脚本语言。不过算来也不是第一次接触,原先喜欢的游戏魔兽世界就是以lua作为脚本语言的。

        lua在的读法是"鲁啊"是月亮的意思。

    ·要点:

    基础语法:

    --常规说明:

        注释方法:单行注释方法是以两个连字符(--)开始一个行注释;

                     块注释方法是以"—[["开始以"]]"(推荐是"—]]")结尾,

                              可在[[之间加任意=,而对应的]]之间也要有相同数量的=;

        程序块:使用关键字do (换行) <body> (换行) end的形式;

        与Python一样不需要使用";"表示语句的结束;

        特殊类型nil表示无效值--为初始化的全局变量就是nil,也可通过赋值nil来删除

            变量--这是垃圾回收器会自动销毁变量;

    类型和值:

        lua中的类型有:nil,boolean,number,string,userdata,function,thread,

                           table(核心,同时也是唯一的数据结构);

        nil:用于表示"无效值"的概念;

        boolean:可选值true/false,其中为假值的情况只有false和nil;

        number:表示实数,lua中没有整数类型;

        userdata:主要用于跟C程序交换数据时使用;

        function:函数--作为"第一类值"来看待;

        thread:协同程序中使用;

        string:不可变值,表示"一个字符序列";

                 完全采用8位编码--可将任意二进制数据存储到一个字符串中;

                 可采用单引号和双引号两种方式定义,支持转移字符;

                 多行字符串定义可采用[=[和]=]形式,其中=数量任意;

                 字符串连接操作符是".."--如果后接数字需要添加空格;

                 系统提供数字和字符串的自动转换;

                 操作符"#"可以获取字符串的长度;

        table:有点像数据表,索引可以是除nil外的任意值;

                可以动态增减大小,是lua中主要且仅有的数据结构机制;

                通过table可以实现数组、符号表、集合、记录、队列等数据结构;

                模块、包和全局空间都是table结构,同时面向对象也通过table实现;

                在Lua中table是"对象"--即使用的是table的应用;

                使用构造表达式"{}"来创建table;

                table永远是"匿名的",变量只是持有的对table的应用;

                访问table元素有两种方法:a["key"]和a.key是等价的;

                需要注意的是table的索引默认的话是从1开始的--这和其他语言不同;

                通过操作符#可获取table的最大数字索引值--需小心索引空隙;

    表达式:

    --算术操作符:有-(负号),+,-,*,/,^(指数),%

                     其中取模%规则是:a%b=a-floor(a/b)*b;

                         对于整数是通常意义上的取模操作,结果符号与b相同;

                         对于实数,x%1得到x的小数部分;

                         对于实数x,x-x%0.01得到x精度到小数点后两位的结果;

    --关系操作符:有<,<=,>,>=,==和~=六个;

                       nil自与自身相等;

                       对table,userdata和fucntion比较引用--引用相同对象才相等;

    --逻辑操作符:有and,or和not三个;

                      and和or都使用短路求值;

                      惯用法:x=x or v用于对未初始化x设置默认值v;

                      惯用法:(a and b) or c类似于C++中的a?b:c语法;

    --字符串连接操作符:操作符".."(两个点)

                      即使两个操作符都是数字也会先将数字转变为字符串在连接;

                      字符串是不可变量,所以连接操作后得到的是新字符串;

    --操作符优先级:^>(not#-)>(*/%)>(+-)>..>(<><=>=~===)>and>or

    --table构造式:列表风格:a={"Sunday","Monday","Tuesday"}

                        记录风格:a={x=10,y=-2}

    流程控制语句:

    --赋值:修改变量或table元素值,更改变量的引用;

               Lua支持多重赋值--可交换变量和函数返回多个值;

    --局部变量与块:通过local语句可以创建局部变量;

    --分支控制if:if语句格式:if cond then

                                       body

                                   elseif cond then

                                       body

                                   else

                                       body

                                   end

    --whil循环:while格式:while cond do

                                      body

                                  end

    --repeat循环:repeat格式:repeat

                                            body

                                        until cond

                     repeat应该是替换C++中的do…while循环;

    --数字型for循环:for var=exp1,exp2,exp3 do

                              body

                          end

    --泛型for循环:通过迭代器函数来遍历所有值;

                     for k[,v] in pairs(t) do

                          body

                     end

    --break和return:

                break语句用于结束一个循环;

                return语句主要用于在函数中返回结果和结束函数执行;

                需要注意的一点是break和return只能是一个块的最后一条语句;

    函数:函数是一种对语句和表达式进行抽象的主要机制;

        在lua中如果参数是字面字符串或table构造式函数调用可以省略括号;

        对象调用方法有两种方式:obj.func(x,y)和obj:func(x,y);

        函数定义方法:function func_name(args)

                                body

                          end

        lua会自动匹配参数和实参--多余舍去不足用nil初始化;

        默认实参:使用参数自动匹配和n=n or 1语句来设置默认参数;

    --多返回值:lua会调整返回值的数量以适应不同的调用情况:

          只有作为表达式最后一个元素时才获取所有参数,否则只得到第一个返回值;

          用到多返回值的情况:多重赋值、函数实参、table构造式、return语句;

          在return语句中,使用括号将强制值返回第一个返回值;

    --变长参数:语法格式:fucntion fucn_name(arg1,…)

                                        body

                                 end

           操作符"..."表示该函数可以接受不同的实参--即变长参数;

           表达式"..."表示一个由所有变长参数构成的数组;

              使用:"a,b=…"的形式或"for i,v in ipairs{…}"或"return …"形式;

           可通过select()函数来访问可变参数"..."中指定位置的参数;

    --具名参数:具名参数是指调用是采用"func_name(a=b)"的形式来指定实参;

           可通过将参数设定为table的方法来间接实现具名参数;

    --匿名函数:定义形式:foo=function (x) return 2*x end的形式;

    --闭合函数:Lua中可在函数中定义函数,且内部函数可访问外部函数的局部变量;

        外部函数的局部变量在内部函数中叫非局部变量--不会在外部函数推出后失效;

        closure(闭合函数)是{函数+该函数所需访问的所有非局部变量};

        同一函数的不同closure的非局部变量是不相干的;

        利用闭合函数(closure)可以进行函数式编程;

    --非全局的函数:指将函数存储在table字段或局部变量中的函数;

        局部函数定义:local f=function (args) body end

                          local function foo(args) body end

        table字段函数:tab.foo=function (args) body end

                           tab={foo=function (args) body end}

                           function tab.foo(args) body end

    --递归函数:在定义递归函数时需要先将函数进行local func初始化;

    --尾调用:只有"return func_name(args)"形式作为结尾才是尾调用;

        尾调用市不会保留上一个函数状态--类似goto不会保留函数调用链;

        一个典型应用是编写"状态机";

    迭代器与泛型for:

        迭代器是一种可以遍历集合中所有元素的机制--主要为for编写;

        迭代器是调用一次返回集合中下一个元素的函数;

        泛型for会做所有的薄记工作,而迭代器只需要正确返回集合中下一个元素;

        迭代器包含两部分:工厂函数如ipairs()函数和工作器函数;

        为了正确返回集合中的下一个元素,迭代器需要保存状态:

            使用closure机制;

            使用泛型for的恒定状态和控制变量来记录;

            扩展恒定状态为table来传递复杂状态;

    编译、执行和错误:

        Lua允许先将源代码预编译为一种中间形式,相关函数:

            dofile():编译并执行lua文件;

            loadfile():编译lua文件但不执行,以函数形式返回而函数调用就是执行;

            loadstring():与loadfile()功能一样,但参数是string;

        底层函数package.loadlib()可以加载动态库;

    --错误处理:

        作为脚本语言,lua把异常处理交给了宿主语言来处理而只提供错误信息;

        错误处理方式:

            返回nil和错误信息;

            通过error()函数抛出异常,相关build-in函数是assert()函数;

            可以用pcall()函数包装需要执行的代码;

    协同程序(coroutine):

        协同程序是一条执行序列,拥有自己独立的栈、局部变量和指令指针,

                   同时与其他协同程序共享全局变量和其他大部分共享资源;

        与线程的区别:

            协同程序像同步后的线程,每次只有一个协同程序在执行;

            协同程序只能自己要求挂起(可通过其他方式改变);

        所有的协同程序相关函数放在"coroutine"的table(库)中;

        协同程序的数据类型为thread类型;

        协同程序的4种状态:

            挂起(suspended):创建和调用yield()后进入该状态;

            运行(running):通过resume()启动协同程序后进入该状态;

            死亡(dead):执行完协同程序后进入此状态;

            正常(normal):调用resume的协同程序处于此状态;

        相关函数:

            coroutine.create(func):创建协同程序;

            coroutine.resume():启动协同程序;

            coroutine.yield():挂起协同程序;

            coroutine.statue():检查协同程序的状态;

        一个比较好的例子是生产者/消费者模型;

    数据结构:

        Lua中的数据结构都是通过table来实现的;

    --数组:使用数字做索引的table;

               需要注意的是Lua中的惯例是索引从1开始的;

    --矩阵和多维数组:

        实现多维数组的两种方法:一种是使用table中嵌套table的方法;

                                     还一种是将二位数组变更为一维后存储在table中;

        因为table本身就是稀疏的,所以用table实现的稀疏表不存在内存开销问题;

    --链表:只需要将table的一个字段持有下一个table的引用就可以了;

    --队列和双向队列:通过table库的插入/删除函数可模拟队列行为;

    --集合与无序组:可将table索引定义为元素,将值定义为真假值来实现集合概念;

    --字符串缓冲:因为字符串是不可变值,像连接操作会产生新字符串开销,

                      可使用table来缓存字符,最后用table.concat()来连接成字符串;

    --图:也可通过table来表示图的概念;

            对应不同的图表示有不同的算法匹配;

    数据文件与持久性:

    --数据文件:

        数据文件指按固定格式(如html/xml等)存储数据的文件;

        lua拥有自己的数据存储方法:Enty{}或Enty{key=value}形式,

                            可在程序中定义Enty函数来处理数据

                            --因为调用table可省略括号所以数据就变成了函数调用;

    --持久性:可将lua代码保存为字符串形式--需要注意转移字符;

    元表与元方法:

        元表:定义了一个table行为属性的另一个table;

        元方法:元表中定义的行为函数;

        table和userdata像类一样可以定义自己的独立元表,

             而其他类型则共享所属类型的元表行为,且需要通过C代码设置;

        相关函数:getmetatable()和setmetatable()两个函数;

    --算术类的元方法:__add,__sub,__mul,__div,__unm(取反),__mod,

                           __pow,__cancat;

        查找元方法顺序:先在第一个值元表中查找,然后是第二个值,都没有会报错;

    --关系类元方法:__eq(==),__lt(<),__le(<=)三种基础比较;

        其他的关系比较操作都是通过上述三种基础比较操作来实现;

        不支持混合比较和拥有不同元方法的比较;

    --库定义的元方法:程序库在元表中定义自己的字段;

        常用字段:metatable.__tostring():printh函数调用时会调用;

                     metatable.__metatable():保护metatable不会被得到/修改;

    --table访问的元方法:提供改变table行为的方法,相关方法:

        __index:查找元素是调用,在访问table中字段时,先在tablez中查找,

                        然后调用__index()函数返回结果,都不存在时返回nil;

                   __index既可以赋值为函数也可以赋值为table;

                   可通过函数rawget来禁用__index的方法;

        __newindex元方法:用于赋值给不存在的字段时调用;

                    当__newindex赋值为table的话,调用__newindex会

                        对绑定的table赋值而不是更改原table;

        使用__index和__newindex可实现:只读table/具有默认值table/继承关系;

     

    环境:具有一定生命期的保存相应数据的table结构;

        lua将所全局变量保存在环境table中--环境table自身保存在全局变量_G中;

        为防止对全局变量的误操作可以给全局环境table设置元表;

        可通过函数setfenv()来设置函数的环境,并可以通过在非全局环境中包含

                全局环境来使用全局变量;

    模块与包:

        模块就是程序库,而包则是一个完整的模块库--整合库/Lua的发行单位;

        模块和包的搜索路径:当前目录=》环境配置目录,会查找lua文件和C文件;

        包采用文件夹结构,需要包含一个init.lua的文件;

    --加载:

        加载函数是require()--当参数是字符串时一般省略括号;

        函数require()会加载模块中的变量和函数等数据结构到全局变量中;

    --编写module:

        在文件开始创建table,中间定义字段函数,最后返回table;

        为了增加模块特性可扩展模块结构;

        新增函数module会自动应用扩展来增强模块特性;

    面向对象编程:

        在Lua中更能体现面向对象是一种思想--通过table实现面向对象编程;

        用惯了C++方式的面向对象方法在使用Lua的面向对象很不习惯;

    --类:

        通过table的元表和__index元方法可实现类的概念;

        通过在元表中定义new方法可实现类的构造函数;

        语法obj.func(obj,args)等价于obj:func(args)--可隐藏self/this;

    --继承:

        单继承的实现通过元表和__index表来实现;

    --多重继承:

        通过__index函数来实现多继承--但以为函数调用存在性能开销;

    --私密性即封装:

        将方法和数据分离放入不同的table中--不常用的技巧;

    弱引用table:

        弱引用—一种会被垃圾收集器忽视的对象引用

        弱引用table:具有弱引用条目的table

        Lua只会回收弱引用table中的对象—而不是值(字符串也是值)

        实现方法是通过元表的__mode字段设置为带k/v的字符串

    --备忘录函数

        根据空间换时间的思想可缓存计算结果来减少频繁操作的耗时

        而缓存的存储则花费空间—即内存

        弱引用可以在结果没有使用时由垃圾回收器自动回收内存

    --对象属性

        将对象作为key来关联属性时,如引用解决了无法删除对象key

        弱引用table的应用--回顾table的默认值

     

    标准库:

    --I/O库:

    --数学库:由一组标准的函数构成:

        三角函数(sin,cos,tan,asin,acos等),指数对数函数(exp,log,log10等),

        取整函数(floor,ceil,max和min),变量pi和huge(最大数字),

        生成伪随机数函数(math.random,math.randomseed)

    --table库:由一些辅助函数构成,这些函数将table作为数组来操作:

        插入和删除(table.insert,table.remove),排序(table.sort),

        连接(table.concat)

    --字符串库:

        原始字符串操作:创建字符串,连接字符串和获取字符串长度

        在5.1中也将string库的函数导出为字符串类型的方法(元表实现)

        表示位置时需要注意的是:起始位置为1且负数表示从结尾计数

        因为字符串是不可变值,所以返回字符串都是新字符串

        基础字符串函数:string.len(s),string.rep(s,n),string.lower(s),

                  string.upper(s),string.sub,string.charstring.byte,

                  t={s:byte(1,-1)},string.char(unpack(t)),string.format;

    --模式匹配函数

      没有采用POSIX和perl的正则表达式方式

      相关函数:

      string.find –找到完全匹配时返回起始索引和结尾索引

        --也可以含开始搜索的开始位置参数

      string.match –与string.find类似,但返回的是子串

      string.gsub –用指定参数替换所有匹配

        --也有可选参数来指定替换次数

      string.gmatch –返回的是函数,用在泛型for中可作为迭代器使用

    模式

      跟正则表达式中的模式是一种概念,但是lua自己的规则

      定义好的字符分类

    .(所有字符)%a(字母)%d(数字)%w(字母和数字字符)

    %l(小写字母)%u(大写字母)

    %c(控制字符)%p(标点符号)%s(空白字符)

    %x(十六进制数字)%z(内部表示为0的字符)

    --大写形式表示的是补集

      魔法字符—需要用%来转义

    --().%+*-?[]^$

    --因为这些字符都有特殊含义需要转义符号%来转义

      自定义字符分类

    --定义方式是用[]将规则放入其中间位置,如二进制[01]

    --可以通过符号”-”表示区间,如八进制[1-7]

    --符号”^”表示取反操作—即得到补集

      重复性修饰符

    + 重复1次或多次

    * 重复0次或多次

    - 也是重复0次或多次

    ? 出现0或1次,表示可选概念

    --其中-和*的区别可以通过例子来理解:

      对于C++中/*和*/,符号*会尽可能多的匹配—即匹配最后*/

      而符号-则尽可能少的扩展来找到第一个*/

      特殊符号

    如果模式以^开头则只会匹配目标字符串的开头部分

    如果模式以$结尾则只会匹配目标字符串的结尾部分

    可使用%b<x><y>来匹配成对的字符

    捕获

      捕获的概念是有选择的从目标字符串中提取匹配内容

      表示为将需要捕获的模式放入在()中

      可以使用”%数字”的形式来引用其他捕获

    替换

      主要是扩展string.gsub函数,即替换的目标可以是函数或table

      当第三个函数或table字段返回nil时不做替换

    技巧

      --没看

    --操作系统库:

    --调试库:

    ·小结:

  • 相关阅读:
    flask 日志级别设置只记录error级别及以上的问题
    UnicodeDecodeError: ‘utf-8’ codec can’t decode byte...
    Python 爬虫使用固定代理IP
    python中json报错:json.decoder.JSONDecodeError: Invalid control character at: line 2 column 18 (char 19)
    scrapy中命令介绍
    Python atexit模块
    MP和OMP算法
    如何理解希尔伯特空间
    压缩感知学习博客推荐
    压缩感知系列文章点评
  • 原文地址:https://www.cnblogs.com/davidyang2415/p/2621504.html
Copyright © 2020-2023  润新知