• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    深入谈谈lua中神奇的table

    前言

    最近在尝试配置 awesome WM,因此粗略地学习了一下 lua 。 在学习过程中,我完全被 table 在 lua 中的应用所镇住了。

    table 在 lua 中真的是无处不在:首先,它可以作为字典和数组来用; 此外,它还可以被用于设置闭包环境、module; 甚至可以用来模拟对象和类

    字典

    table 最基础的作用就是当成字典来用。 它的 key 值可以是除了 nil 之外的任何类型的值。

    t={}
    t[{}] = "table"  -- key 可以是 table
    t[1] = "int"  -- key 可以是整数
    t[1.1] = "double" -- key 可以是小数
    t[function () end] = "function" -- key 可以是函数
    t[true] = "Boolean" -- key 可以是布尔值
    t["abc"] = "String" -- key 可以是字符串
    t[io.stdout] = "userdata" -- key 可以是userdata
    t[coroutine.create(function () end)] = "Thread" -- key可以是thread

    当把 table 当成字典来用时,可以使用 pairs 函数来进行遍历。

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

    运行结果为:

    1 ->  int
    1.1 ->  double
    thread: 0x220bb08 ->  Thread
    table: 0x220b670  ->  table
    abc ->  String
    file (0x7f34a81ef5c0) ->  userdata
    function: 0x220b340 ->  function
    true  ->  Boolean

    从结果中你还可以发现,使用 pairs 进行遍历时的顺序是随机的,事实上相同的语句执行多次得到的结果是不一样的。

    table 中的key最常见的两种类型就是整数型和字符串类型。 当 key 为字符串时,table可以当成结构体来用。同时形如 t["field"] 这种形式的写法可以简写成 t.field 这种形式。

    数组

    当 key 为整数时,table 就可以当成数组来用。而且这个数组是一个 索引从1开始 ,没有固定长度,可以根据需要自动增长的数组。

    a = {}
    for i=0,5 do  -- 注意,这里故意写成了i从0开始
     a[i] = 0
    end

    当将 table 当成数组来用时,可以通过 长度操作符 # 来获取数组的长度

    print(#a)

    结果为

    5

    你会发现, lua 认为 数组 a 中只有5个元素,到底是哪5个元素呢?我们可以使用使用 ipairs 对数组进行遍历:

    for i,v in ipairs(a) do
     print(i,v)
    end

    结果为

    1 0
    2 0
    3 0
    4 0
    5 0

    从结果中你会发现 a 的0号索引并不认为是数组中的一个元素,从而也验证了 lua 中的数组是从 1开始索引的

    另外,将table当成数组来用时,一定要注意索引不连贯的情况,这种情况下 # 计算长度时会变得很诡异

    a = {}
    for i=1,5 do
     a[i] = 0
    end
    a[8] = 0   -- 虽然索引不连贯,但长度是以最大索引为准
    print(#a)
    a[100] = 0   -- 索引不连贯,而且长度不再以最大索引为准了
    print(#a)

    结果为:

    8
    8

    而使用 ipairs 对数组进行遍历时,只会从1遍历到索引中断处

    for i,v in ipairs(a) do
     print(i,v)
    end

    结果为:

    1 0
    2 0
    3 0
    4 0
    5 0

    环境(命名空间)

    lua将所有的全局变量/局部变量保存在一个常规table中,这个table一般被称为全局或者某个函数(闭包)的环境。

    为了方便,lua在创建最初的全局环境时,使用全局变量 _G 来引用这个全局环境。因此,在未手工设置环境的情况下,可以使用 _G[varname] 来存取全局变量的值.

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

    rawequal  ->  function: 0x41c2a0
    require ->  function: 0x1ea4e70
    _VERSION  ->  Lua 5.3
    debug ->  table: 0x1ea8ad0
    string  ->  table: 0x1ea74b0
    xpcall  ->  function: 0x41c720
    select  ->  function: 0x41bea0
    package ->  table: 0x1ea4820
    assert  ->  function: 0x41cc50
    pcall ->  function: 0x41cd10
    next  ->  function: 0x41c450
    tostring  ->  function: 0x41be70
    _G  ->  table: 0x1ea2b80
    coroutine ->  table: 0x1ea4ee0
    unpack  ->  function: 0x424fa0
    loadstring  ->  function: 0x41ca00
    setmetatable  ->  function: 0x41c7e0
    rawlen  ->  function: 0x41c250
    bit32 ->  table: 0x1ea8fc0
    utf8  ->  table: 0x1ea8650
    math  ->  table: 0x1ea7770
    collectgarbage  ->  function: 0x41c650
    rawset  ->  function: 0x41c1b0
    os  ->  table: 0x1ea6840
    pairs ->  function: 0x41c950
    arg ->  table: 0x1ea9450
    table ->  table: 0x1ea5130
    tonumber  ->  function: 0x41bf40
    io  ->  table: 0x1ea5430
    loadfile  ->  function: 0x41cb10
    error ->  function: 0x41c5c0
    load  ->  function: 0x41ca00
    print ->  function: 0x41c2e0
    dofile  ->  function: 0x41cbd0
    rawget  ->  function: 0x41c200
    type  ->  function: 0x41be10
    getmetatable  ->  function: 0x41cb80
    module  ->  function: 0x1ea4e00
    ipairs  ->  function: 0x41c970

    从lua 5.2开始,可以通过修改 _ENV 这个值(lua5.1中的setfenv从5.2开始被废除)来设置某个函数的环境,从而让这个函数中的执行语句在一个新的环境中查找全局变量的值。

    a=1    -- 全局变量中a=1
    local env={a=10,print=_G.print} -- 新环境中a=10,并且确保能访问到全局的print函数
    function f1()
     local _ENV=env
     print("in f1:a=",a)
     a=a*10   -- 修改的是新环境中的a值
    end
    
    f1()
    print("globally:a=",a)
    print("env.a=",env.a)
    in f1:a= 10
    globally:a= 1
    env.a= 100

    另外,新创建的闭包都继承了创建它的函数的环境

    module

    lua 中的模块也是通过返回一个table来供模块使用者来使用的。 这个 table中包含的是模块中所导出的所有东西,包括函数和常量。

    定义module的一般模板为

    module(模块名, package.seeall)

    其中 module(模块名) 的作用类似于

    local modname = 模块名
    local M = {}                    -- M即为存放模块所有函数及常数的table
    _G[modname] = M
    package.loaded[modname] = M
    setmetatable(M,{__index=_G})    -- package.seeall可以使全局环境_G对当前环境可见
    local _ENV = M                  -- 设置当前的运行环境为 M,这样后续所有代码都不需要限定模块名了,所定义的所有函数自动变成M的成员
    函数定义以及常量定义>

    return M                        -- module函数会帮你返回module table,而无需手工返回

    对象

    lua 中之所以可以把table当成对象来用是因为:

    函数在 lua 中是一类值,你可以直接存取table中的函数值。 这使得一个table既可以有自己的状态,也可以有自己的行为:

    Account = {balance = 0}
    function Account.withdraw(v)
     Account.balance = Account.balance - v
    end

    lua 支持闭包,这个特性可以用来模拟对象的私有成员变量

    function new_account(b)
     local balance = b
     return {withdraw = function (v) balance = balance -v end,
      get_balance = function () return balance end
     }
    end
    
    a1 = new_account(1000)
    a1.withdraw(10)
    print(a1.get_balance())

    990

    不过,上面第一种定义对象的方法有一个缺陷,那就是方法与 Account 这个名称绑定死了。 也就是说,这个对象的名称必须为 Accout 否则就会出错

    a = Account
    Account = nil
    a.withdraw(10)     -- 会报错,因为Accout.balance不再存在

    为了解决这个问题,我们可以给 withdraw 方法多一个参数用于指向对象本身

    Account = {balance=100}
    function Account.withdraw(self,v)
     self.balance = self.balance - v
    end
    a = Account
    Account = nil
    a.withdraw(a,10)     -- 没问题,这个时候 self 指向的是a,因此会去寻找 a.balance
    print(a.balance)

    90

    不过由于第一个参数 self 几乎总是指向调用方法的对象本身,因此 lua 提供了一种语法糖形式 object:method(...) 用于隐藏 self 参数的定义及传递. 这里冒号的作用有两个,其在定义函数时往函数中地一个参数的位置添加一个额外的隐藏参数 sef, 而在调用时传递一个额外的隐藏参数 self 到地一个参数位置。 即 function object:method(v) end 等价于 function object.method(self,v) end, object:method(v) 等价于 object.method(object,v)

    当涉及到类和继承时,就要用到元表和元方法了。事实上,对于 lua 来说,对象和类并不存在一个严格的划分。

    当一个对象被另一个table的 __index 元方法所引用时,table就能引用该对象中所定义的方法,因此也就可以理解为对象变成了table的类。

    类定义的一般模板为:

    function 类名:new(o)
     o = o or {}
     setmetatable(o,{__index = self})
     return o
    end

    或者

    function 类名:new(o)
     o = o or {}
     setmetatable(o,self)
     self.__index = self
     return o
    end

    相比之下,第二种写法可以多省略一个table

    另外有一点我觉得有必要说明的就是 lua 中的元方法是在元表中定义的,而不是对象本身定义的,这一点跟其他面向对象的语言比较不同。

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

    您可能感兴趣的文章:
    • Lua Table转C# Dictionary的方法示例
    • Lua中设置table为只读属性的方法详解
    • Lua编程示例(一):select、debug、可变参数、table操作、error
    • 举例讲解Lua中的Table数据结构
    • Lua table中安全移除元素的方法
    • Lua的table库函数insert、remove、concat、sort详细介绍
    • C++遍历Lua table的方法实例
    • Lua中释放table占用内存的方法
    • Lua中table的遍历详解
    • Lua中获取table长度问题探讨
    • Lua中获取table长度的方法
    • Lua中table里内嵌table的例子
    • Lua面向对象编程之基础结构table简例
    上一篇:OpenResty中正则模式匹配的2种方法详解
    下一篇:lua读取redis数据的null判断示例代码
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    深入谈谈lua中神奇的table 深入,谈谈,lua,中,神奇,的,