Lua中每个值都可具有元表。 元表是普通的Lua表,定义了原始值在某些特定操作下的行为。你可通过在值的原表中设置特定的字段来改变作用于该值的操作的某些行为特征。例如,当数字值作为加法的操作数时,Lua检查其元表中的"__add"字段是否有个函数。如果有,Lua调用它执行加法。

我们称元表中的键为事件(event),称值为元方法(metamethod)。前述例子中的事件是"add",元方法是执行加法的函数。

可通过函数getmetatable查询任何值的元表。

可通过函数setmetatable替换表的元表。不能从Lua中改变其他类型的元表(除了使用调试库);必须使用C API才能做到。

表和完整的用户数据具有独立的元表(尽管多个表和用户数据可共享元表);每种其他类型的所有值共享一个元表。所以,所有数字共享一个元表,字符串也是,等等。

元表可以控制对象的数学运算、顺序比较、连接、取长、和索引操作的行为。元表也能定义用户数据被垃圾收集时调用的函数。Lua给这些操作的每一个都关联了称为事件的特定键。当Lua对某值执行其中一个操作时,检查该值是否含有元表以及相应的事件。如果有,与该键关联的值(元方法)控制Lua如何完成操作。

元表控制后面列举的操作。每个操作由相应的名字标识。每个操作的键是由其名字前缀两个下划线“__”的字符串;例如,操作“加(add)”的键是字符串"__add"。这些操作的语义通过一个Lua函数描述解释器如何执行操作作了更好的说明。

下面显示的Lua代码只是说明性的;真实的行为被硬编码到解释器中,并且比这里的模拟更加高效。这些描述中的所有函数(rawget、tonumber等等。)在§5.1中描述。特别一提,要获取给定对象的元方法,我们使用表达式

metatable(obj)[event]

它应被解读为

rawget(getmetatable(obj) or {}, event)

就是说,访问一个元方法不会调用其他元方法,而且访问没有元表的对象不会失败(只是结果为nil)。

"add": + 操作。
下面的getbinhandler函数定义Lua如何选择二元操作的处理程序。首先尝试第一操作数,如果它的类型没有定义该操作的处理程序,则尝试第二操作数。 function getbinhandler (op1, op2, event)
return metatable(op1)[event] or metatable(op2)[event]
end 通过应用该函数,op1 + op2的行为是 function add_event (op1, op2)
local o1, o2 = tonumber(op1), tonumber(op2)
if o1 and o2 then -- 两操作数都是数字
return o1 + o2 -- ‘+’此处是‘add’的原语
else -- 至少一个操作数不是数字
local h = getbinhandler(op1, op2, "__add")
if h then -- 用两个操作数调用处理程序
return (h(op1, op2))
else -- 没有可用的处理程序:缺省行为
error(...)
end
end
end "sub": - 操作。 行为类似于“add”操作。
"mul": * 操作。 行为类似于“add”操作。
"div": / 操作。 行为类似于“add”操作。
"mod": % 操作。 行为类似于“add”操作。以o1 - floor(o1/o2)*o2为操作原语。
"pow": ^ (取幂)操作。 行为类似于“add”操作,以函数pow(来自C数学库)为操作原语。
"unm": 一元-操作。
function unm_event (op)
local o = tonumber(op)
if o then -- 操作数是数字?
return -o -- ‘-’此处是‘unm’的原语
else -- 操作数不是数字
-- 尝试由操作数取得处理程序。
local h = metatable(op).__unm
if h then-- 用操作数调用处理程序
return (h(op))
else -- 没有可用的处理程序:缺省行为
error(...)
end
end
end "concat": .. (连接)操作。
function concat_event (op1, op2)
if (type(op1) == "string" or type(op1) == "number") and
(type(op2) == "string" or type(op2) == "number") then
return op1 .. op2 -- 字符串连接原语
else
local h = getbinhandler(op1, op2, "__concat")
if h then
return (h(op1, op2))
else
error(...)
end
end
end "len": # 操作。
function len_event (op)
if type(op) == "string" then
return strlen(op) -- 取字符串长度原语
elseif type(op) == "table" then
return #op -- 取表长度原语
else
local h = metatable(op).__len
if h then -- 用操作数调用处理程序
return (h(op))
else -- 没有可用的处理程序:缺省行为
error(...)
end
end
end "eq": == 操作。 函数getcomphandler定义Lua如何选择比较操作符的元方法。只有待比较的两个对象类型和选定操作对应的元方法都相同,才会选择该元方法。
function getcomphandler (op1, op2, event)
if type(op1) ~= type(op2) then return nil end
local mm1 = metatable(op1)[event]
local mm2 = metatable(op2)[event]
if mm1 == mm2 then
return mm1
else
return nil
end
end
"eq"事件定义如下:
function eq_event (op1, op2)
if type(op1) ~= type(op2) then -- 类型不同?
return false -- 对象不同
end
if op1 == op2 then -- 相等原语?
return true -- 对象相同
end -- 尝试元方法
local h = getcomphandler(op1, op2, "__eq")
if h then
return (h(op1, op2))
else
return false
end
end
a ~= b等价于not (a == b)。 "lt": < 操作。
function lt_event (op1, op2)
if type(op1) == "number" and type(op2) == "number" then
return op1 < op2 -- 数字比较
elseif type(op1) == "string" and type(op2) == "string" then
return op1 < op2 -- 词典顺序比较
else
local h = getcomphandler(op1, op2, "__lt")
if h then
return (h(op1, op2))
else
error(...);
end
end
end
a > b等价于b < a。 "le": <= 操作。
function le_event (op1, op2)
if type(op1) == "number" and type(op2) == "number" then
return op1 <= op2 -- 数字比较
elseif type(op1) == "string" and type(op2) == "string" then
return op1 <= op2 -- 词典顺序比较
else
local h = getcomphandler(op1, op2, "__le")
if h then
return (h(op1, op2))
else
h = getcomphandler(op1, op2, "__lt")
if h then
return not h(op2, op1)
else
error(...);
end
end
end
end
a >= b等价于 b <= a。注意,假定a <= b等价于not (b < a),那么当没有“le”元方法时,Lua尝试“lt”。 "index": 索引访问table[key]。
function gettable_event (table, key)
local h
if type(table) == "table" then
local v = rawget(table, key)
if v ~= nil then
return v
end
h = metatable(table).__index
if h == nil then
return nil
end
else
h = metatable(table).__index
if h == nil then
error(...);
end
end
if type(h) == "function" then
return (h(table, key)) -- 调用处理程序
else
return h[key] -- 对它重复上述操作
end
end "newindex": 索引赋值table[key] = value。
function settable_event (table, key, value)
local h
if type(table) == "table" then
local v = rawget(table, key)
if v ~= nil then
rawset(table, key, value);
return
end
h = metatable(table).__newindex
if h == nil then
rawset(table, key, value);
return
end
else
h = metatable(table).__newindex
if h == nil then
error(...);
end
end
if type(h) == "function" then
h(table, key,value) -- 调用处理程序
else
h[key] = value -- 对它重复上述操作
end
end "call": 当Lua调用值时被调用。
function function_event (func, ...)
if type(func) == "function" then
return func(...) -- 调用原语
else
local h = metatable(func).__call
if h then
return h(func, ...)
else
error(...)
end
end
end

  

Lua中的元表和元方法的更多相关文章

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

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

  2. Lua中的元表与元方法

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

  3. lua编程之元表与元方法

    一. 前言 lua是一种非常轻量的动态类型语言,在1993年由由Roberto Ierusalimschy.Waldemar Celes 和 Luiz Henrique de Figueiredo等人 ...

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

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

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

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

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

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

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

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

  8. Lua中的元表(metatable)、元方法(metamethod)详解

    在第一次看见这两样东西的时候,可能会觉得它很深奥,但其实很好理解,虽然实际上它可能真的很深奥.(小若:停!滚粗.) 1.知道为什么1 + 1 = 2吗? 为什么在Lua中,1+1会等于2呢?(小若:难 ...

  9. lua元表与元方法

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

随机推荐

  1. 为什么可以这么快! awk 与python的应用

    这几天刚处理一个排序问题 源文件: 可以看到有11G大小,需要根据最后一列的热度来做一下排序.如果让你来做这样的排序,在linux环境下,你会如何处理呢? xch27@lanzhou:/asrdata ...

  2. Three.js基础探寻八——法向材质与材质的纹理贴图

    4.法向材质 法向材质可以将材质的颜色设置为其法向量的方向,有时候对于调试很有帮助. 法向材质的设定很简单,甚至不用设置任何参数: new THREE.MeshNormalMaterial() 材质的 ...

  3. Gitlab基本管理<一>

    一. 创建Gitlab中第一个项目 1. Gitlab项目的可见类型有三种级别. Private project: 该级别是只有项目拥有者或者已经得到授权的人可以访问该项目,或者这些人是该项目组的成员 ...

  4. AngularJS Intellisense in Visual Studio 2012

    Recently, a lot of people have asked for Intellisense support for AngularJS in the Visual Studio HTM ...

  5. 【转】eval()函数用法

    eval 功能:将字符串str当成有效的表达式来求值并返回计算结果. 语法: eval(source[, globals[, locals]]) -> value 参数: source:一个Py ...

  6. shell 文件传 参数

    n cross-platform, lowest-common-denominator sh you use: #!/bin/sh value=`cat config.txt` echo " ...

  7. 抓包分析LVS-NAT中出现的SYN_RECV

    CIP:192.168.10.193 VIP:192.168.10.152:8000 DIP:100.10.8.152:8000 RIP:100.10.8.101:8000 和 100.10.8.10 ...

  8. 关于django 京东淘宝 混合搜索实现原理

    混合搜索在各大网站如京东.淘宝都有应用,他们的原理都是什么呢?本博文将为你介绍它们的实现过程. 混合搜索的原理,用一句话来说就是:关键字id进行拼接. 混合搜索示例: 数据库设计: 视频方向: 1 2 ...

  9. BZOJ2599 IOI2011Race

    题目:一棵树,每条边有权.求一条路径,权值和等于K,且边的数量最小. 点分治,我们考虑经过根节点的路径,t[x]表示路径长为x时最少的边数,然后每次拿栈记下来清空. 注意先搜索再更新,这样可以避免同一 ...

  10. SPOJ694 DISUBSTR --- 后缀数组 / 后缀自动机

    SPOJ694 DISUBSTR 题目描述: Given a string, we need to find the total number of its distinct substrings. ...