一、 前言

lua是一种非常轻量的动态类型语言,在1993年由由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo等人发明,lua的设计目标是轻便地嵌入宿主语言,增强系统的可扩展性和可定制性。lua的源码只有两万余行,非常精简小巧,在目前的脚本引擎中,lua的速度是最快的,这也是lua进入程序设计语言前20名,如今已经广泛应用于游戏行业,这几篇文章将会讨论下lua的几个比较重要的特性。

一门语言的类型系统是其最根本的特征,所以本文先从与lua的类型系统关系最紧密的元表和元方法谈起。作为一门轻量级语言,lua的核心非常精简,它的基本类型只有8种:nil,boolean,number,string,userdata,function,thread和table,其中table是唯一的数据结构,是lua中最重要的类型,可以作为其他数据结构的基础,如数组,链表,队列和集合等都可以通过table实现。更强大的是,lua还为table提供了自定义操作的功能。在c++等面向对象语言中,类的可操作行为由成员函数决定。lua中,元方法就是table的“成员函数”,为不同的table提供特殊的操作行为,元表是元方法的集合。通过元表和元方法,table可以直接实现类,继承等面向对象特性。

二、 元表元方法介绍

lua中每个值其实都有元表,不过每个table和userdata都可以有自己专有的元表,(userdata是宿主中的数据结构,可以使用宿主语言的方法,为了限制过度对其使用元表,不能在lua脚本中直接设置,需通过lua_setmetatable创建,这里不讨论),而其他类型的预定义操作都在一个共享的元表中,新的table默认没有元表,必须通过setmetatable和getmetatable设置和查询元表。

  1. t = {}
  2. assertgetmetatablet)== nil
  3. t1 = {}
  4. setmetatable(t, t1)
  5. assert(getmetatable(t) == t1)

在元表中定义的函数就是元方法,table的元方法分为算数类,关系类,库定义和访问类的元方法。

1. 算数类元方法

lua的算数类元方法都有对应的字段名,包括__add, __mul,__sub, __div,__mod和__pow等,下面示例了如何定义两个table的加法操作,

  1. a = { "a1", "a2","a3" }
  2. b = { "b1", "b2", "b3" }
  3. meta = { }
  4. meta.__add = function(t1, t2)
  5. t = { }
  6. for k, v in ipairs(t1) do
  7. table.insert(t, v)
  8. end
  9. for k, v in ipairs(t2) do
  10. table.insert(t, v)
  11. end
  12. return t
  13. end
  14. setmetatable(a, meta)
  15. c = a+b
  16. for _,v in ipairs(c) do
  17. print(v)
  18. end

上面代码中只需要给表a设置了元表,表b没有元表也能正常运行,这与lua查找元表的顺序有关系。lua先查找第一个table,如果有元表并且其中有 __add方法就调用该方法,不关心第二个table有没有元表;否则查找第二个table有没有__add的元方法,有就调用第二个table的元方法;如果都没有这个元方法就引发一个错误。

2. 关系类元方法

关系类元方法只有等于__eq,小于__lt和小于等于__le这3个操作,其他3个会自动转化,如a>b会自动转为b<a.

  1. a = { "a1", "a2" }
  2. b = { "b1", "b2", "b3" }
  3. meta = { }
  4. meta.__le = function(a, b)
  5. for k in pairs(a) do
  6. if not b[k] then return false end
  7. end
  8. return true
  9. end
  10. meta.__lt = function(a, b)
  11. return a<=b and not (b<=a)
  12. end
  13. meta.__eq = function(a, b)
  14. return a<=b and b<=a
  15. end
  16. setmetatable(a, meta)
  17. setmetatable(b, meta)
  18. print(a<=b)
  19. print(a<b)
  20. print(a>=a)
  21. print(b>a)
  22. print(b<a)

与算法类元方法不同,table必须具有相同的元方法才能用于比较操作。

3. 库定义的元方法

上面的元方法都是lua核心具有的,是lua虚拟机定义的,除此之外,各种程序库也会用自己的字段定义元方法,比如print总是调用table的tostring方法,

  1. a = { "a1", "a2" }
  2. meta.__tostring = function(a)
  3. local l = { }
  4. for _,k in pairs(a) do
  5. l[#l+] = k;
  6. end
  7. return "{"..table.concat(l, ",").."}"
  8. end
  9. setmetatable(a, meta)
  10. printa

4. 访问类元方法

访问元方法使用最普遍的是__index和__newindex。一般当访问一个table中不存在的元素时会返回nil,但是如果table具有__index元方法,就不返回nil而是调用这个元方法。利用__index可以方便地实现继承,

  1. mt = { }
  2. mt.__index = function(t, k)
  3. return base[k]
  4. end
  5.  
  6. base = { b1 = , b2 = , b3 = }
  7. derive = { d = }
  8.  
  9. setmetatable(derive, mt)
  10.  
  11. print(derive.b1)
  12. print(derive.d)

当对table中不存在的索引赋值时就会调用__newindex元方法,

  1. mt = { }
  2. mt.__newindex = function(t, k, v)
  3. base[k] = v
  4. end
  5. base = { b1 = , b2 = , b3 = }
  6. derive = { d1 = }
  7. setmetatable(derive, mt)
  8. derive["d2"] =
  9. print(base.d2)

三、 元表元方法实例

下面是一个使用元方法的实例,用于产生迭代递增表,

  1. T = { container = { } }
  2.  
  3. T.mt = {
  4.  
  5. __add = function(a, b)
  6. local c = T.new{}
  7. for k,v in pairs(T.new(a)) do
  8. c[k] = v
  9. end
  10. for k,v in pairs(T.new(b)) do
  11. c[k] = v
  12. end
  13. return c
  14. end,
  15.  
  16. __sub = function(a, b)
  17. local c = T.new{}
  18. for k,v in pairs(T.new(a)) do
  19. c[k] = v
  20. end
  21. for k,v in pairs(T.new(b)) do
  22. c[k] = nil
  23. end
  24. return c
  25. end,
  26.  
  27. __tostring = function(a)
  28. local l = { }
  29. for k in pairs(a) do
  30. l[#l+] = k;
  31. end
  32. return "{"..table.concat(l, ",").."}"
  33. end
  34. }
  35.  
  36. T.new = function(t)
  37. if (t == nil) then t = {} end
  38. if (getmetatable(t) == T.mt) then return t end
  39. local r = {}
  40. for _, b in ipairs(t) do
  41. r[tostring(b)] = true
  42. end
  43. setmetatable(r, T.mt)
  44. return r
  45. end
  46.  
  47. T.print = function(t)
  48. for k, v in pairs(t.container) do
  49. print(k)
  50. print(v)
  51. end
  52. end
  53.  
  54. local mt = {
  55. __newindex = function(t, k, v)
  56. t.container[k] = T.new(v)
  57. end,
  58.  
  59. __index = function(t, k)
  60. return t.container[k]
  61. end,
  62. }
  63.  
  64. setmetatable(T, mt)
  65.  
  66. T["first"] = { "a1", "b1"}
  67. print("elements in table first")
  68. T.print(T)
  69. T["second"] = T["first"] + { "a2", "b2", "a3", "b3"}
  70. print("elements in table first and second")
  71. T.print(T)
  72. T["third"] = T["second"] - { "a3", "b3" }
  73. print("elements in table first, second and third")
  74. T.print(T)

lua编程之元表与元方法的更多相关文章

  1. Lua中的元表与元方法学习总结

    前言 元表对应的英文是metatable,元方法是metamethod.我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了"+"符号,就可以进行类的加法运算.在Lu ...

  2. Lua中的元表与元方法

    [前言] 元表对应的英文是metatable,元方法是metamethod.我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了“+”符号,就可以进行类的加法运算.在Lua中也有这个道理 ...

  3. lua中 table 元表中元方法的重构实现

    转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...

  4. Lua中的元表和元方法

    Lua中每个值都可具有元表. 元表是普通的Lua表,定义了原始值在某些特定操作下的行为.你可通过在值的原表中设置特定的字段来改变作用于该值的操作的某些行为特征.例如,当数字值作为加法的操作数时,Lua ...

  5. Lua 学习笔记(十一)元表与元方法

    在Lua中的每个值都有一套预定义的操作集合.例如可以将数字相加,可以连接字符串,还可以在table中插入一对key-value等.但是我们无法将两个table相加,无法对函数作比较,也无法调用一个字符 ...

  6. 【游戏开发】小白学Lua——从Lua查找表元素的过程看元表、元方法

    引言 在上篇博客中,我们简单地学习了一下Lua的基本语法.其实在Lua中有一个还有一个叫元表的概念,不得不着重地探讨一下.元表在实际地开发中,也是会被极大程度地所使用到.本篇博客,就让我们从Lua查找 ...

  7. lua元表与元方法

    lua中提供的元表(metatable)与元方法(metamethod)是一种非常重要的语法,metatable主要用于做一些类似于C++重载操作符式的功能. lua中提供的元表是用于帮助lua变量完 ...

  8. lua metatable和metamethod元表和元方法

    Lua中提供的元表是用于帮助Lua数据变量完成某些非预定义功能的个性化行为,如两个table的相加.假设a和b都是table,通过元表可以定义如何计算表达式a+b.当Lua试图将两个table相加时, ...

  9. Step By Step(Lua元表与元方法)

    Step By Step(Lua元表与元方法) Lua中提供的元表是用于帮助Lua数据变量完成某些非预定义功能的个性化行为,如两个table的相加.假设a和b都是table,通过元表可以定义如何计算表 ...

随机推荐

  1. SDN 第一次上机作业

    第一题 拓扑: 测试连通性: 第二题 拓扑: 测试连通性: 第三题 拓扑: 测试连通性:

  2. Crond定时任务

    crond简介 crond是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动crond ...

  3. 【原创】rabbitmq 学习

    rabbitmq 命令 1. 用户管理类命令: 该类别比较意图比较明显,详细查看官方文档.现做俩点说明: authenticate_user 此命令用于验证一个用户名和密码对不对,并没有什么用: se ...

  4. 【译文】InnoDB 的不同的SQL如何加锁

    http://dev.mysql.com/doc/refman/5.6/en/innodb-locks-set.html 前置:检索如果用不到索引,会扫描全表,并根据策略加锁.所以,这就是我们合理建立 ...

  5. java 扁平化输出json所有节点key/value

    本章主要介绍用java实现扁平化输出json所有节点key/value(包含所有内层子节点) 1.json结构 目的输出bill_list下的datalist里的子节点key/value 2.实现代码 ...

  6. c# datetime用法总结

    备忘:YYYY-mm-dd HH:MM:SS部分解释 d 月中的某一天.一位数的日期没有前导零. dd 月中的某一天.一位数的日期有一个前导零. ddd 周中某天的缩写名称,在 Abbreviated ...

  7. mongodb的学习-4-使用 MongoDB shell 来连接 Mongodb 服务

    执行启动操作后,mongodb 在输出一些必要信息后不会输出任何信息,之后就等待连接的建立,当连接被建立后,就会开始打印日志信息. 使用 MongoDB shell 来连接 Mongodb 服务 标准 ...

  8. 703. Kth Largest Element in a Stream

    题目来源: https://leetcode.com/problems/kth-largest-element-in-a-stream/ 自我感觉难度/真实难度: 题意: 这个题目的意思解读了半天,没 ...

  9. 使用CSS3的“transition ”属性控制长宽度的缓慢变化

    有时候我们可能会想要改变某个资源信息的长宽度,比如改变某个div的宽度,而且需要让这个宽度缓慢改变,而不是突然就改变了.这时候你可能会想到使用jquery的animate()函数,不过这个方法既得引用 ...

  10. RabbitMQ如何保证发送端消息的可靠投递-发生镜像队列发生故障转移时

    上一篇最后提到了mandatory这个参数,对于设置mandatory参数个人感觉还是很重要的,尤其在RabbitMQ镜像队列发生故障转移时. 模拟个测试环境如下: 首先在集群队列中增加两个镜像队列的 ...