Lua之元表
Lua之metatable
一、元表
Lua中的每个值都有一套预定义的操作集合,也可以通过metatable(元表)来定义一个值的行为,metatable包含了一组meatmethod(元方法)。
Lua中的每个值都有一个metatable,table和userdata可以有各自独立的metatable,而其他类型的值则共享其类型所属的单一metatable。
在Lua代码中,只能设置table的metatable,若要设置其他类型的值的metatable,必须通过C代码来完成。
|
function |
description |
|
getmetatable(t) |
获取t的metatable |
|
setmetatable(t, m) |
设置t的metatable为m |
Lua标准库中只有string库有默认metatable,其它类型在默认情况下都没有metatable。
getmetatable('ab') -- table: 0x1b684b0
getmetatable() -- nil
getmetatable({}) -- nil
下面是一个例子,用table模拟集合操作,并利用metatable实现了交集、并集运算:
Set = {}
local mt = {} -- metatable
function Set.new(l)
local set = {}
setmetatable(set, mt) -- set metatable for all "set" type
for _,v in ipairs(l) do set[v] = true end
return set
end
function Set.union(a,b)
local res = Set.new{}
for k in pairs(a) do res[k] = true end
for k in pairs(b) do res[k] = true end
return res
end
function Set.intersection(a,b)
local res = Set.new{}
for k in pairs(a) do res[k] = b[k] end
return res
end
function Set.sub(a, b)
for k in pairs(a) do
if not b[k] then return false end
end
return true
end
function Set.tostring(set)
local l = {}
for e in pairs(set) do
l[#l + ] = e
end
return "{" .. table.concat(l, ",") .. "}"
end
mt.__add = Set.union
mt.__mul = Set.intersection
mt.__le = Set.sub
mt.__tostring = Set.tostring
s1 = Set.new{,,,}
s2 = Set.new{, }
s3 = Set.new{,}
print(getmetatable(s1)) -- 0x12e6cb0
print(getmetatable(s2)) -- 0x12e6cb0
print(s1+s2) -- {1,30,10,50,20}
print(s1*s2) -- {30}
print(s3<=s1) -- true
在metatable中,每种算术操作都有对应的字段名,除了上述的__add和__mul外,还有其它的一些字段,如下表:
|
metamethods |
description |
|
__add |
+ |
|
__sub |
- |
|
__mul |
* |
|
__div |
/ |
|
__unm |
负 |
|
__pow |
幂 |
|
__mod |
模 |
|
__concat |
定义连接行为 |
|
__eq |
== |
|
__lt |
< |
|
__le |
<= |
|
__tostring |
供print函数调用 |
|
__metatable |
如果设置了该字段,将禁止查看、修改该元表 |
metatable中关系运算符只支持 ==、< 、<=,Lua会将 a~=b转化为 not (a==b),将a>b转化为b<a,将a>=b转化为b<=a。
当一个算术表达式中混合了具有不同metatable的值时,例如
s = Set.new{,,}
s = + s
Lua会按照如下步骤查找metatable
1、如果第一个值有metatable,并且有__add字段,那么就使用该字段作为meatmethod;
2、如果第一个值没有metatable,但第二个值有metatable,并且有__add字段,那么就使用该字段作为meatmethod;
3、如果两个值都没有metatable,Lua就引发一个错误。
因此,上例会调用 Set.union,但在Set.union 函数内部会发生错误!
与算术类的meatmethod不同,关系类的meatmethod不能应用于混合的类型。如果试图将一个字符串与一个数字作顺序比较,Lua会引发一个错误。同样,如果试图比较两个具有不同meatmethod的对象,Lua也会引发一个错误。
注意,等于比较永远不会引发错误。如果两个对象具有不同的meatmethod,直接返回false。
二、访问table的元方法
当访问table中一个不存在的字段时,会调用其metatable的__index元方法,如果没有这个元方法,返回nil。否则,就由__index元方法提供查找结果。
Window = {}
Window.prototype = {x=, y=, width=, height=}
Window.mt = {}
Window.mt.__index = function(table, key)
return Window.prototype[key]
end
function Window.new(o)
setmetatable(o, Window.mt)
return o
end
w1 = Window.new({x=, y=, width=})
print(w1.width) --
w2 = Window.new({x=, y=})
print(w2.width) -- 100
print(rawget(w1, 'width')) -- 37
print(rawget(w2, 'width')) -- nil
注意:如果不想使用元表,可以使用rawget方法,它对table进行一次不考虑元表的简单访问。
在Lua中,将__index元方法用于继承是很普遍的方法,__index元方法不必一定是函数,也可以是一个table:
当__index是一个函数时,以table和不存在的key作为参数来调用该函数;
当__index是一个table时,Lua就以相同的方式来重新访问这个table,因此,上面的例子的__index元方法也可以如下:
Window.mt.__index = Window.prototype
当对table中一个不存在的索引赋值时,会查找__newindex元方法,如果有这个元方法,就调用它,而不是执行赋值。
如果__newindex是一个table,就在此table中执行赋值,而不是对原来的table。
类似rawget,调用rawset(t,k,v),可以不涉及任何元方法而直接设置t[k]=v。
组合使用__index和__newindex元方法可以实现一些强大的功能,比如:只读table,具有默认值的table、面向对象编程中的继承等。
例子一、具有默认值的table:
local mt = {__index=function() return t.___ end}
function setDefault(t,d)
t.___ = d
setmetatable(t, mt)
end
tab = {x=, y=}
print(tab.x, tab.z) --10, nil
setDefault(tab, )
print(tab.x, tab.z) -- 10, 0
注意: __index和__newindex都是在table中没有所需访问的索引时才发挥作用的。
例子二,通过一个空table来代理对原table的访问,从而来跟踪对table的访问过程:
t = {,,}
local _t = t
t = {} -- proxy
local mt = {
__index = function(t,k) print("access to element " .. tostring(k)) return _t[k] end,
__newindex = function(t,k,v) print("update of element " .. tostring(k) .. " to " .. tostring(v)) _t[k] = v end,
}
setmetatable(t, mt)
t[] = "hello"
print(t[])
输出为:
update of element to hello
access to element
hello
例子三,实现一个只读table:
function readOnly(t)
local proxy = {}
local mt = {
__index = t,
__newindex = function(t,k,v) error("attempt to update a read-only table", ) end
}
setmetatable(proxy, mt)
return proxy
end
days = readOnly{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}
print(days[])
days[] = "No" -- error
Lua之元表的更多相关文章
- lua metatable(元表)
lua metatable (元表) 概述 普通 table 类型的表仅能够做一些单表操作, 无法进行一些特殊的以及两个表的操作, 比如 table1 + table2, print(table3) ...
- Lua的元表(metatable)
metatable允许我们改变table的行为 > Lua中的每一个表都可以有metatable(后面我们将看到userdata也有Metatable) > Lua默认创建一个不带meta ...
- Lua中元表的学习
--table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作. --元表(Metatable),允许我们改变table的行为,可以对两个table进行操作 -- ...
- lua 的元表与元方法
1. 元表与元方法, 相当于C++的函数重载 参考链接: https://blog.csdn.net/yueya_shanhua/article/details/52233228
- lua编程之元表与元方法
一. 前言 lua是一种非常轻量的动态类型语言,在1993年由由Roberto Ierusalimschy.Waldemar Celes 和 Luiz Henrique de Figueiredo等人 ...
- lua 元表是个啥?
function readOnly(t) local proxy = {} local mt = { __index = t, __newindex = function(t,k,v) error(& ...
- Step By Step(Lua元表与元方法)
Step By Step(Lua元表与元方法) Lua中提供的元表是用于帮助Lua数据变量完成某些非预定义功能的个性化行为,如两个table的相加.假设a和b都是table,通过元表可以定义如何计算表 ...
- Metatable让我从心认知了Lua(相知篇)
自从在公司和Lua第一次相遇之后,我的IT生涯从此也开启了另一个新篇... 起初并没有和Lua产生相遇时的那种电石火花般的怦然心动...但... 说实话虽然我的心没有因此触动但至少也不排斥...因为公 ...
- lua元方法
lua中有元表的概念,元表类似于基类的功能, 在元表中有两个方法可以很好的认识元表: __index和__newindex __index用于查询 对表中的字段进行访问时,如果该表有元表,并且 表中没 ...
随机推荐
- 自定义View完全解析
自定义View主要包括以下3种方式: 一.组合控件,利用已有控件的组合,来满足自己的需求. 例子:顶部导航栏 二.继承已有View,比如继承TextView.ImageView等,根据需要重写相应的方 ...
- C#-ado.net-属性扩展
属性扩展 是封装好的类中添加更多属性,此方法可以添加更多的数据运算方法,更灵活便捷 上一篇中介绍的是实体类和数据访问类,实体类封装的内容如下: 执行查询语句后,结果如下: 我们可以看到,性别显示的是t ...
- Mac OS 下 eclipse中文乱码解决方法(eclipse for mac 中文乱码)
由于一些java源码是从其他人那里拷贝过来,放入Mac os 版本的eclipse下,发现中文都是乱码.经过小试,可以解决. 1.打开eclipse 偏好设置 2.General ——>Cont ...
- iOS之类的本质
1.本质 类的本质其实也是一个对象(类对象) 程序中第一次使用该类的时候被创建,在整个程序中只有一份. 此后每次使用都是这个类对象,它在程序运行时一直存在. 类对象是一种数据结构,存储类的基本信息:类 ...
- Reveal使用步骤
一.Reveal使用步骤 1.启动Reveal --> Help --> Show Reveal Library in Finder,拖动添加Reveal.framework到工程中. 选 ...
- js-FCC算法-Pairwise
找到你的另一半 都说优秀的程序员擅长面向对象编程,但却经常找不到另一半,这是为什么呢?因为你总是把自己局限成为一个程序员,没有打开自己的思维. 这是一个社群的时代啊,在这里你应该找到与你有相同价值观但 ...
- UVA11426 欧拉函数
大白书P125 #include <iostream> #include <cstring> using namespace std; #define MMX 4000010 ...
- Linux VFS Extended Attribute And Access Control Table
catalog . 简介 . 扩展属性 . 访问控制表 . 小结 0. 简介 许多文件系统都提供了一些特性,扩展了VFS层提供的标准功能,虚拟文件系统不可能为所有特性都提供具体的数据结构.超出标准的U ...
- GitBash上传代码不计入贡献的问题处理
发现最近写的代码,通过GitBash上传到github,但是格子确一个都没亮,今天通过一番检索解决了此问题,特做记录: 通过这篇文章找到了原因所在: GitHub 更新代码到底怎样才算贡献? 通过这篇 ...
- 一次pthread_kill引发的HA切换
记录这个坑 程序设计是这样的 socket server主线程A接收新来的连接,然后新建一个线程B用于处理数据接收,由B线程启动一个线程C进行数据的发送处理. 当 B线程出现异常,在异常处理过程中,会 ...