【Lua/基础】Lua基础知识

Lua基础

(挖个坑待整理…)

lua读作“撸啊”,游戏开发中热更神器,无论是初级开发者还是高级开发者都需要熟练掌握的一门语言,因为它小巧实用,无孔不入。

  • 一定要学习lua的几个理由

    1. 可扩展性
      lua最出名的特点用于游戏中的热更新另外还有游戏插件,同样是脚本语言,为什么只有lua受此青睐,有大神给出回答
      总结来说lua的虚拟机很小,其和谐的底层实现在进程乃至线程级都没有污染,lua在一开始就被设计为很容易与传统的C/C++整合的语言,它严格限制了自己要解决的问题,从而把语言特性限制在一个非常有限的范围之内。
    2. 精简
      其精简体现在两个方面,一是和python一样语法代码量极少,二是由于语言被定位为扩展性强,虚拟机和语言特性都为精简而生。
    3. 高效率
      lua是平均效率最快的脚本语言,这里有关于lua和python的比较讨论
    4. 跨平台性
      Lua 不是通过使用条件编译实现平台无关,而是完全使用ANSI (ISO) C,这意味着只要你有ANSI C 编译器你就可以编译并使用Lua。
  • Windows下环境搭建

    • 手动编译
      1. 首先到官网下载想要的版本源码并解压
      2. 使用VS的命令行工具cd到刚刚解压的src路径下,然后输入或使用批处理执行以下命令,这里的版本可以替换成你下载的对应版本
        cl /MD /O2 /c /DLUA_BUILD_AS_DLL *.c
        ren lua.obj lua.o
        ren luac.obj luac.o
        link /DLL /IMPLIB:lua5.3.0.lib /OUT:lua5.3.0.dll *.obj
        link /OUT:lua.exe lua.o lua5.3.0.lib
        lib /OUT:lua5.3.0-static.lib *.obj
        link /OUT:luac.exe luac.o lua5.3.0-static.lib
      3. 将src文件夹中的lua.exe、lua5.3.0.dll、luac.exe拷贝到lua-5.3.0文件夹中;
      4. 在lua-5.3.0文件夹中新建lib文件夹,将src中lua5.3.0.exp、lua5.3.0.lib、lua5.3.0-static.lib、luac.exp、luac.lib拷贝到lib文件夹中;
      5. 在lua-5.3.0文件夹中新建include文件夹,将src中lauxlib.h、lua.h、lua.hpp、luaconf.h、lualib.h拷贝到include文件夹中;
      6. 设置环境变量,新建系统变量
        LUA_DIR —> F:\Lua\lua-5.3.0
        LUA_PATH —> ?.lua;%LUA_DIR%\lua\?.lua
        LUA_CPATH —> ?.dll;%LUA_DIR%\?.dll
        在Path变量后添加;F:\Lua\lua-5.3.0
      7. 打开命令行,输入lua回车,出现如下提示,说明成功
        Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio
    • 搜索Lua for windows,附带IDE。

    关于编辑器个人推荐VSCode,需要语法提示可以下载插件”lua”

  • 基本类型
    nil
    boolean
    number
    string
    function:由 C 或 Lua 编写的函数
    userdata: 表示任意存储在变量中的C数据结构
    thread : 表示执行的独立线路,用于执行协同程序
    table : 关联数组(lua最特别的类型)。

  • 字符串
    单行注释–
    多行注释–[[ ]]
    字符串:”“,”,[[]]
    使用 .. 来连接两个字符串或数字
    tostring(var) 转为字符串
    tonumber(var) 转为数字

  • 函数
    function function_name( … )
    – body
    end
    lua中的函数是一个很灵活的存在,可以作为参数或返回值,可以像其它类型一样存储在变量中。

    当函数参数只有一个时,且这个参数是字符串或者表构造的时候,()可有可无

    函数的传参和返回和赋值操作一样遵循自动匹配
    如:function GetAorB(a,b) return a,b end
    使用()进行额外包裹可以强制只返回一个值,如:(GetAorB(1,2)) –结果是1

  • 标准库
    标准库包括string 库、table 库、I/O 库、OS 库、算术库、debug 库。标准库都是由C实现的

  • 运算符
    不等:~=
    或:or
    且:and
    非:not
    三元运算:(a and b) or c –如果a为真则输出b否则输出c,与js不同,这里的b和c都不能为执行语句,但是可以为表达式


  • 表是lua比较强大的体现之一,使用{}格式
    在lua中,表既是数据结构,又是对象
    当它作为数据结构时,可以作为数组,作为对象时,其字段可以存储数值、其它表甚至是函数。
    因此在使用表时可以作为两种形态处理。
    对象方式: local tb={a=1,b=2} –其中a,b作为字段来看待
    键值对: local tb={[“a”]=1,[“b”]=2} –其中a,b必须打上引号,作为一个具体的索引值对待,如果不打引号则表示一个已经声明的变量,是将变量a,b中存储的值作为索引值。
    数组:local tb={1,2,3,4} –lua的数组索引从1开始,这和很多语言不同

    那么访问和赋值也对应三种方式
    tb.a=function(){} –将字段a存储一个匿名函数,使用tb.a()调用
    tb[“a”]=nil –将索引值为字符”a”的元素赋值为nil,等同删除作用
    tb[1]={4,3,2,1} –将第一个元素赋值一个数组实现多维数组

    其实数组形式也可以看做是索引值为特殊值的键值对,只不过他们在赋值时按顺序进行了有序分配

  • 赋值
    lua允许使用x, y = y, x赋值的方式
    相当于先读取变量y和x中的值再对应分别赋值给x和y
    lua中不能使用++或+=

  • 流程控制语句
    条件:
    if conditions then
    –body
    end;
    while循环:
    while condition do
    –statements;
    end;
    repeat-until循环:
    repeat
    –statements;
    until conditions;

    注意这里与js的do while可不同,until后的条件是停止条件,而js中do while的while后面是执行条件,刚好相反。

    for循环:
    for var=exp1,exp2,exp3 do
    loop-part
    end
    exp1表达式:初始化赋值
    exp2表达式:迭代长度
    exp3表达式:默认为1,为迭代值大小

    获取数组长度使用”#”如:local arr={1,2,3,4}; #arr –为4

    遍历:ipairs 和 pairs
    //挖个坑,待扩展

    终止关键字:break、 return

  • 加载库
    loadfile与loadstring 可以加载库或者函数

    require 加载库(常用)

    1. require 会搜索目录加载文件
    2. require 会判断是否文件已经加载避免重复加载同一文件。

    loadlib 加载动态链接库
    loadlib 函数加载指定的库并且连接到Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为Lua 的一个函数,这样我们就可以直接在Lua
    中调用他。

    一般情况下我们期望二进制的发布库包含一个与前面代码段相似的stub 文件,安装二进制库的时候可以随便放在某个目录,只需要修改stub 文件对应二进制库的实际路径即可。将stub 文件所在的目录加入到LUA_PATH,这样设定后就可以使用require 函数加载C 库了。

  • 异常处理
    error(errmsg) 函数 错误抛出
    assert(func,errmsg)断言函数
    assert 首先检查第一个参数,若没问题,assert 不做任何事情;否则,assert 以第二个参数作为错误信息抛出。第二个参数是可选的。

    pcall(func) 在保护模式(protected mode)下执行函数内容,同时捕获所有的异常和错误。若一切正常,pcall 返回true 以及“被执行函数”的返回值;否则返回false 和错误信息。
    (相当于把其它语言的try catch封装在一个函数中)
    xpcall(func,callback) 增加了一个出错时处理错误信息的回调

  • coroutine协程
    coroutine.create (f):创建一个主体函数为 f 的新协程。 f 必须是一个 Lua 的函数。 返回这个新协程,它是一个类型为 “thread” 的对象。
    coroutine.isyieldable ():如果正在运行的协程可以让出,则返回真。不在主线程中或不在一个无法让出的 C 函数中时,当前协程是可让出的
    coroutine.resume (co [, val1, ···]):开始或继续协程 co 的运行。 当你第一次延续一个协程,它会从主体函数处开始运行。 val1, … 这些值会以参数形式传入主体函数。 如果该协程被让出,resume 会重新启动它; val1, … 这些参数会作为让出点的返回值。如果协程运行起来没有错误, resume 返回 true 加上传给 yield 的所有值 (当协程让出), 或是主体函数的所有返回值(当协程中止)。 如果有任何错误发生, resume 返回 false 加错误消息。

    简单来说
    1.在resume中的val第一次会作为f的参数传入,之后resume中的val会作为f中yield的返回值而不再是f的参数
    2.f中返回的值会从resume中返回出
    3.f中yield传入的值也会从resume中返回
    总之,数据会以val1…的通道从f的参数或yield返回处传给f内部,而f则把内部数据通过yield的参数口返回出来,或者直接return来终止了,如果yield了n次,那么resume n+1次挂起的协程才会终止,终止状态为dead

    coroutine.running ():返回当前正在运行的协程加一个布尔量。 如果当前运行的协程是主线程,其为真。
    coroutine.status (co):以字符串形式返回协程 co 的状态: 当协程正在运行(它就是调用 status 的那个) ,返回 “running”; 如果协程调用 yield 挂起或是还没有开始运行,返回 “suspended”; 如果协程是活动的,都并不在运行(即它正在延续其它协程),返回 “normal”; 如果协程运行完主体函数或因错误停止,返回 “dead”。
    coroutine.wrap (f):创建一个主体函数为 f 的新协程。 f 必须是一个 Lua 的函数。 返回一个函数, 每次调用该函数都会延续该协程。 传给这个函数的参数都会作为 resume 的额外参数。 和 resume 返回相同的值, 只是没有第一个布尔量。 如果发生任何错误,抛出这个错误。
    coroutine.yield (···):挂起正在调用的协程的执行。 传递给 yield 的参数都会转为 resume 的额外返回值。

    使用多重协程可以实现 生产-过滤/加工-消费 模式,消费者作为驱动,在消费者中调用resume(filter,producer) 第一次将一个生产者传入过滤器中,filter和producer都是协程,后面每次调用resume(filter)都会驱动在filter中的resume(producer)调用,并把resume(producer)的返回值(产品)经过筛选加工后最终商品用yield(goods)传出到消费者中

    使用coroutine.wrap能返回一个函数来直接调用协程,相当于把resume封装在一个函数中并返回了这个函数的引用。然而由于不能直接接触到创建的协程,缺少灵活性,没有办法知道wrap所创建的协同的状态,也没有办法检查错误的发生。

  • coroutine+LuaSockect进行网络通信
    对每个连接使用一个cortoutine进行管理

  • 马尔可夫链算法
    是指数学中具有马尔可夫性质的离散事件随机过程。该过程中,在给定当前知识或信息的情况下,过去(即当前以前的历史状态)对于预测将来(即当前以后的未来状态)是无关的。在马尔可夫链的每一步,系统根据概率分布,可以从一个状态变到另一个状态,也可以保持当前状态。
    可以理解为每一步都是完全随机,不受之前结果的影响。

  • Lua中构造器概念
    使用Entry函数+table信息来初始化一个lua对象。Entry { args }


  • array 、list 、queue 、stack 、hashtable等 表在lua中是个非常重要的存在

  • 数据描述
    数据描述有两种形式:物理描述和逻辑描述。物理数据描述指数据在存储设备上的存储方式的描述,物理数据是实际存放在存储设备上的数据。逻辑数据描述指程序员或用户以操作的数据形式的描述,是抽象的概念化数据。
    在数据处理中,数据描述将涉及到不同的范畴。从事物的特性到计算机中的具体表示,实际上经历了三个阶段:概念设计中的数据描述、逻辑设计中的数据描述和物理存储介质中的数据描述。
    如lua中描述一个表时就没有js中转为json字符串那么方便了,需要进行封装处理

  • 元编程相关 metatable与metamethods
    metatable
    元表可以用于描述一个表或多个表的相关信息
    lua默认创建一个不带metatable的新表
    使用getmetatable可以读取一个表关联的metatable
    使用setmetatable可以设置或更改一个表的metatable

    metamethods 通常用来重写运算符
    lua中使用__methodname来区别

  • 环境environment
    lua把环境(包含所有全局变量)存储在一个全局变量_G中
    全局环境与非全局环境

  • string
    lua中的标准库提供了很多实用的string方法,用于处理字符。

  • 命名空间
    由于function methodname () –body end 实际上等同于 methodname=function() –body end
    为避免命名污染,通常使用 function table.methodname() –body end 的方式创建成员函数,使用local table={}
    或者用local function methodname () –body end

  • package

    1. 使用一个全局变量的table作为package供其他包加载(最好在最后面加上return package这样在加载这个包时可以多种选择来操作这个包)
      p1:
      pack={
      sayhi=function() print(“hi”) end
      }
      return pack
      p2:
      local pack=requir(“p1”)
      pack.sayhi()–“hi”

    注意:推荐在声明变量的时候都加上local, 再通过一个table把私有变量和table中的变量对应,向外提供公有方法
    如:
    local function sayhi() print(“hi”) end
    return {
    sayhi=sayhi
    }

    2.setfenv —5.1 使用传入表来代替当前环境,这样即便忘了写local也没关系

  • 面向对象
    在lua中一个table可以看做是一个类的实例
    在使用一些成员方法时,往往需要用到这个实例本身的引用,也就是所谓this,lua中可以使用”:”和self来完成对实例本身的访问,”:”和”.”的作用相同,区别在于

    1. 使用”.”进行成员方法声明或调用时,都需要显示地传入对象实例。
      table.method(table)–调用
      function table.method(self) end
    2. 使用”:”进行成员方法声明或调用时则是隐式的
      table:method( )–调用
      function table:method( ) return self end

    但是调用方式并不需要完全对应声明方式,不需要同时都使用”.”或”:”,但必须遵守各自的规则(1和2)。
    如:table.method(table)==>function table:method() return self end
    或者 table:method()==> function table.method(self) end
    都是可以的

    原型prototype
    一个tableA的metatable.__index指向的tableB可以看做是tableA的原型,而tableA可以看做是tableB的一个实例
    function Account:new (o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
    end
    自然,一个table既可以是metatable也可以是原型,也就是它本身__index指向的table
    在调用tableA上的方法或属性时,如果没有找到对应的目标则会去原型上找,这样看起来就像一种继承关系

    那么,tableB自然也继承了tableA的new方法
    假设在创建一个新的tableC,通过层层继承,tableC在调用一个方法的时候查询顺序就变成了tableC=>tableB=>tableA
    当然,由于查询顺序,父类中有的方法子类中可以进行重写。
    另外在查询方法过程中只关心方法名而不会关心形参个数,即便基类中对应有形参个数相同的方法也不会去访问。

    一个table既能作为一个类又能作为一个实例。

  • lua中的私有化实现
    之前在package中说过,如果要保证一个模块中某些东西的私有化,可以把需要对外暴露的部分放在一个table中作为全局table或直接匿名返回,其它所有变量都使用local声明
    在lua的类或者table中也是,如果某些方法只是作为私有方法,那么在一个全局function中把所有其它私有table或属性或方法都使用local声明,把需要对外提供的信息使用一个table返回。

  • Weak表
    weak表的存在是为了帮助垃圾回收机制更好地工作
    一个weak tables 是指所有引用都是weak 的table。这意味着,如果一个对象只存在于weak tables 中,Lua 将会最终将它收集。
    表的weak 性由他的metatable 的__mode 域来指定的。在这个域存在的时候,必须是个字符串:如果这个字符串包含小写字母‘k’,这个table 中的keys 就是weak 的;如果这个字符串包含小写字母‘v’,这个table 中的vaules 就是weak 的

    在记忆函数中的应用—针对loadstring加载的函数结果缓存
    在关联对象属性的应用
    综合应用—重述带有默认值的表

  • 标准库
    math库 string库 table库 IO库 OS库 Debug库

  • C API

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页