Chinaunix首页 | 论坛 | 博客
  • 博客访问: 685617
  • 博文数量: 132
  • 博客积分: 10060
  • 博客等级: 上将
  • 技术积分: 1732
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-21 12:35
个人简介

迷惘的码农。

文章分类

全部博文(132)

文章存档

2013年(1)

2011年(2)

2010年(9)

2009年(41)

2008年(79)

我的朋友

分类:

2008-08-15 11:12:32

2.8 -

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

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

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

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

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

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

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

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

     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
阅读(2042) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~