`
7090
  • 浏览: 278475 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

(转)lua元表

    博客分类:
  • Lua
 
阅读更多
本文简译自一篇老外的博客,写得不错可惜我翻译的太烂,简译如下。

(key--value常见翻译为“键值对”,我翻译为索引、值)

在这篇教程里我会介绍Lua中一个重要的概念: metatable(元表),掌握元表可以让你更有效的

使用Lua。 每一个tabel都可以附加元表, 元表是带有索引集合的表,它可以改变被附加表的行为。

看下例:

t = {} -- 普通表
mt = {} -- 元表,现在暂时什么也没有
setmetatable(t, mt) -- 把mt设为t的元表
getmetatable(t) -- 这回返回mt

如你所见 getmetatable 和setmetatable 是主要的函数。 当然我们可以把上面的三行代码合为:

t = setmetatable({}, {})

setmetatable 返回第一个参数, 因此我们可以使用这个简短的表达式。现在,我们在元表里放些什

么呢? 元表可以包含任何东西,但是元表通常以"__"(两个下划线)开头的索引(当然string类型)

来调用,例如__index和__newindex。 和索引对应的值可以是表或者函数,例如:

t = setmetatable({}, {
  __index = function(t, key)
    if key == "foo" then
      return 0
    else
      return table[key]
    end
  end
})
我们给__index索引分配了一个函数, 让我们来看看这个索引是干啥的。



__index

元表里最常用的索引可能是__index,它可以包含表或函数。

当你通过索引来访问表, 不管它是什么(例如t[4], t.foo, 和t["foo"]), 以及并没有分配索引的值时,

Lua 会先在查找已有的索引,接着查找表的metatable里(如果它有)查找__index 索引。 如果

__index 包含了表, Lua会在__index包含的表里查找索引。 这听起来很迷糊,让我们看一个例子。

other = { foo = 3 }
t = setmetatable({}, { __index = other })
t.foo -- 3 ,现在__index包含的表{foo=3}查找
t.bar -- nil ,没找到

如果__index 包含一个函数,当被它调用时,会把被访问的表和索引作为参数传入。从上面的例子来看,

我们可以使用带有条件语句的索引,以及任意的Lua语句。因此在这种情况下,如果索引和字符串"foo"

相等,我们可以返回0,否则,我们可以查询表中被使用的索引;当"foo"被使用时,让t作为table的

别名并返回0。(这句不是太懂,原文为:Therefore,in that example, if the key was equal to

the string "foo" we would return 0, otherwise we look up the table table with the key that

was used; this makes t an alias of table that returns 0 when the key "foo" is used.)

你可能会疑问,怎么把表作为是第一个传给__index 函数的参数。当你在多个表里使用相同的元表时,

这会很方便,并支持代码复用和节省电脑资源。我们会在最下面的Vector 类里看到解释。

--注:下面是我的一个例子

other = function(t,k) if k=="foo" then return 0 end end
t = setmetatable({}, { __index = other })
print(t.foo)



__newindex

下一个是__newindex, 它和__index类似。和 __index一样,它可以包含函数和表。当你给表中不存在

的值赋值时,Lua会在metatable里查找__newindex,调用顺序和 __index一样。如果__newindex是表,

索引和值会设置到指定的表:

other = {}
t = setmetatable({}, { __newindex = other })
t.foo = 3 --t里没有foo,查看__newindex,并把foo=3传给了other,并没有给t里的foo赋值
other.foo – 3 故为3
t.foo – nil 故为 nil
和期望的一样,__newindex 是函数时,当被调用时会传递表、索引、值三个参数。

t = setmetatable({}, {
  __newindex = function(t, key, value)
    if type(value) == "number" then
      rawset(t, key, value * value)
    else
      rawset(t, key, value)
    end
  end
})

t.foo = "foo"
t.bar = 4
t.la = 10
t.foo -- "foo"
t.bar -- 16
t.la -- 100

当在t里创建新的索引时,如果值是number,这个值会平方,否则什么也不做。下面介绍rawget 和rawset。



rawget 和 rawset

有时需要get 和set表的索引,不想使用metatable.你可能回猜想, rawget 允许你得到索引无需__index,

rawset允许你设置索引的值无需__newindex (不,相对传统元表的方式,这些不会提高速度)。为了避免陷

在无限循环里,你才需要使用它们。 在上面的例子里, t[key] = value * value将再次调用__newindex

函数,这让你的代码陷入死循环。使用rawset(t, key, value * value) 可以避免。

你可能看到,使用这些函数, 我们必须传递参数目标table, key, 当你使用rawset时还有value。



操作符

许多元表的索引是操作符 (如, +, -, 等),允许你使用表完成一些操作符运算。例如,我们想要一个表支持

乘法操作符(*), 我们可以这样做:

t = setmetatable({ 1, 2, 3 }, {
  __mul = function(t, other) ,
    new = {}
   
    for i = 1, other do
      for _, v in ipairs(t) do table.insert(new, v) end
    end
   
    return new
  end
})

t = t * 2 -- { 1, 2, 3, 1, 2, 3 }

这允许我们创建一个使用乘法操作符重复某些次数的新表。你也看的出来, __mul和乘法相当的索引是,

与__index、 __newindex 不同,操作符索引只能是函数。 它们接受的第一个参数总是目标表, 接着

是右值 (除了一元操作符“-”,即索引__unm)。下面是操作符列表:

__add: 加法(+)
__sub: 减法(-)
__mul: 乘法(*)
__div: 除法(/)
__mod: 取模(%)
__unm: 取反(-), 一元操作符
__concat: 连接(..)
__eq: 等于(==)
__lt: 小于(<)
__le:小于等于(<=)
(只有==, <, <= ,因为你能通过上面的实现所有操作,事实上== 和<就足够了)



__call

接下来是__call 索引, 它允许你把表当函数调用,代码示例:

t = setmetatable({}, {
  __call = function(t, a, b, c, whatever)
    return (a + b + c) * whatever
  end
})

t(1, 2, 3, 4) –- 24 ,表t在调用时先查找__call,调用里面的函数,t便相当于函数了
和通常一样在call里的函数,被传递了一个目标表,还有一些参数。__call 非常有用,经常用来在表和它

里面的函数之间转发调用(原文it's used for is forwarding a call on a table to a function inside

that table.)。 kikito的 tween.lua 库就是个例子tween.start可以被自身调用(tween). 另一个例子是

MiddleClass, 类里的new函数可以被类自身调用。



__tostring

最后一个是 __tostring。如果实现它,那么tostring 可以把表转化为string, 非常方便类似print的函数

使用。 一般情况下,当你把表转为string时, 你需要"table: 0x<hex-code-here",但是你可以仅用

__tostring来解决。示例:

t = setmetatable({ 1, 2, 3 }, {
  __tostring = function(t)
    sum = 0
    for _, v in pairs(t) do sum = sum + v end
    return "Sum: " .. sum
  end
})

print(t) -- prints out "Sum: 6"


创建一个向量类

下面我们来封装一个2D 向量类(感谢 hump.vector 的大量代码)。代码太长你可以查看gist #1055480,

代码里有大量的metatable概念,(注意,如果你之前没接触面向对象可能会有点难)。

Vector = {}
Vector.__index = Vector
首先声明了一个Vector class, 设置了__index 索引指向自身。 这在干啥呢?你会发现我们把所有的元表

放到Vector类里了。你将看到在Lua里实现OOP (Object-Oriented Programming)的最简单方式。Vector

表代表类, 它包含了所有方法,类的实例可以通过Vector.new (如下) 创建了。

function Vector.new(x, y)
  return setmetatable({ x = x or 0, y = y or 0 }, Vector)
end
它创建了一个新的带有x、y 属性的表, 然后把metatable设置到Vector 类。我们知道Vector 包含了所有的

元方法,特别是 __index。这意味着我们通过新表可以使用所有Vector里方法。

另外重要的一行是:

setmetatable(Vector, { __call = function(_, ...) return Vector.new(...) end })
这意味着我们可以创建一个新的Vector 实例通过 Vector.new或者仅Vector。

最后重要的事,你可能没注意冒号语法。当我们定义一个带有冒号的函数时,如下:

function t:method(a, b, c)
  -- ...
end
我们真正定义的是这个函数:

function t.method(self, a, b, c)
  -- ...
end
这是一个语法糖,帮助我们使用OOP。当调用函数时,我们可以这样使用冒号语法:

-- these are the same
t:method(1, 2, 3)
t.method(t, 1, 2, 3)
我们如何使用 Vector 类? 示例如下:

a = Vector.new(10, 10)
b = Vector(20, 11)
c = a + b
print(a:len()) -- 14.142135623731
print(a) -- (10, 10)
print(c) -- (30, 21)
print(a < c) -- true
print(a == b) -- false
因为Vector里有__index,我们可以在实例里使用它的所有方法。



结论

感谢阅读,我希望你学到了一些东西. 如果你有建议或疑问, 请留下评论comments section,我想听到你的回复!

原文:http://www.cnblogs.com/xdao/archive/2013/04/02/lua-metatable.html

作者:半山
出处:http://www.cnblogs.com/xdao/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分享到:
评论

相关推荐

    Excel到Lua转表工具

    《Excel到Lua转表工具详解》 在编程领域,数据管理是至关重要的环节,而Excel作为数据处理的常用工具,其灵活性和易用性深受广大用户的喜爱。然而,在游戏开发或者自动化脚本编写中,我们可能需要将Excel的数据转换...

    json转lua-table工具

    JSON(JavaScript Object Notation)和Lua Table 是两种广泛使用的数据序列化格式,分别在Web开发和游戏编程领域中占据重要地位。JSON因其简洁明了的结构而被广泛用于数据交换,而Lua Table则是Lua编程语言中的核心...

    excel表转Lua脚本工具

    《Excel表转Lua脚本工具详解》 在IT行业中,数据转换是一项常见的任务,尤其当涉及到配置文件或者游戏脚本时。Lua作为一种轻量级、高效且易读的脚本语言,常被用于游戏开发和其他配置文件的编写。然而,手动编写Lua...

    excel转lua工具

    Excel转Lua工具是一种实用的开发辅助软件,它能够帮助开发者将结构化的Excel表格数据转换成Lua脚本语言,从而简化数据管理和程序逻辑的编写过程。在游戏开发、配置管理或其他需要大量数据处理的项目中,这种工具尤为...

    Csv转Lua工具(纯Lua版)

    用Lua语言写的一个配置文件转换工具,能够将csv配置文件转换为Lua“类文件”。支持的格式有数字、字符串、数字数组、字符数组和table等。内有详细使用说明。水平有限,如有Bug或者好的建议欢迎留言交流。

    lua文本转成json文本

    lua json文件转换 数据编排 lua配置表转成 json配置表

    LUAC解密工具.zip_andlua解密工具_andlua解密软件_lua 4.2解密_luac转lua_lua解密工具

    "luac转lua"是解密过程的核心,即把LUAC编译的字节码文件转换回源代码文件。这个过程需要对Lua的字节码结构有深入理解,因为字节码并不直接对应源代码的文本形式。解密工具通过逆向工程实现这个转换,帮助开发者理解...

    Excel配置转Lua文件

    "Excel配置转Lua文件"这个标题表明了一个工具或程序,它的主要功能是将Excel表格中的数据转换为Lua语言的格式,以便于在游戏或者其他需要Lua脚本的应用中使用。这样的工具极大地简化了数据管理和导入过程,减少了...

    LuaXml工具

    1. **XML解析**:LuaXML提供了解析XML文档的API,可以将XML字符串或文件转换为 Lua 数据结构,如表(table)。这样,用户可以方便地遍历、访问和修改XML文档的内容。 2. **事件驱动解析**:LuaXML可能采用了事件...

    使用 xls_to_lua.lua 把 xls 解析成 lua table

    解析文件使用 lua 编写,把 *.xls 解析成 lua table,双击 run.bat 文件值行解析,支持多子表解析,键值对 k/v 解析。 例子说明:https://blog.csdn.net/qq_26117171/article/details/127131168 支持解析的数据类型...

    json转lua格式脚本

    json转lua脚本,全自动转换 快速读取

    lua加密成luac工具

    ios版本将lua加密成luac

    Excel 转 lua

    标题 "Excel 转 lua" 描述了一个工具,它能够将Excel文件的数据转换为Lua脚本语言的格式。这个工具的使用需要Python环境,并且依赖于`xlrd`库,这是一个用于读取Excel文件的Python库。在压缩包中,已经包含了Python...

    Excel表格批量转CSV、Lua工具

    "Excel表格批量转CSV、Lua工具"就是为了满足这种需求而设计的。 这个工具包含两个部分:`xls2csv`和`xls2lua`,它们分别负责将Excel文件转换为CSV和Lua格式。CSV是一种通用的数据交换格式,易于读取和写入,适合...

    unity里面使用编辑器把json文件转换成lua文件

    可以把json转换成lua文件放在工程里面 只需要指定文件读取位置 和 存放位置即可

    LUAC反编译_LUC_lua反编译工具_luac_luac解密工具_Lua解密_

    反编译的过程并非完全可逆,因为字节码中丢失了某些源代码中的元信息,如注释和原始变量名。因此,反编译的结果可能不完全等同于原Lua源代码,可能会出现变量名被替换为临时编号,逻辑结构稍有变化等情况。尽管如此...

    Excel转换成Js或者Lua代码_Exceljslua_excel转js_excel转为lua_provideik2_

    通常,我们需要将Excel数据转换为CSV格式,然后利用Lua的字符串处理功能读取并解析CSV,转化为Lua表结构。或者,可以编写一个中间转换器,将Excel数据先转为JSON,再由JSON转为Lua。 "provideik2"标签可能指的是一...

    emoji.lua:Lua的基本表情符号支持模块

    表情符号 :speech_balloon:Lua的基本表情符号支持模块 :crescent_moon:例子 local emoji = require ( " emoji " )print (emoji. emojify ( " I :heart: :tea:! " ))-- &gt; "I :red_heart: :teacup_without_handle:!...

    c#对象转lua表工具

    可以将任意c#对象转为lua表 方便将策划的配置直接转换为lua表 自己是用在unity游戏项目的编辑器工具里面

Global site tag (gtag.js) - Google Analytics