Lua中的元表和元方法

Lua中的元表和元方法,第1张

概述Lua中的元表和元方法 Lua中每个值都可具有元表。 元表是普通的Lua表,定义了原始值在某些特定 *** 作下的行为。你可通过在值的原表中设置特定的字段来改变作用于该值的 *** 作的某些行为特征。例如,当数字值作为加法的 *** 作数时,Lua检查其元表中的"__add"字段是否有个函数。如果有,Lua调用它执行加法。 我们称元表中的键为事件(event),称值为元方法(metamethod)。前述例子中的事件是"a Lua中的元表和元方法

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,"__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,255); Font-family:CourIEr"> if (type(op1) == "string" or type(op1) == "number") and 
 (type(op2) == "string" or type(op2) == "number") then  
  return op1 .. op2  -- 字符串连接原语       
 else  

  if h then    
  else    
   error(...)  
end   


"len": # *** 作。 

function len_event (op)
 if type(op) == "string" then  
  return strlen(op)  -- 取字符串长度原语       
 elseif type(op) == "table" then  
  return #op  -- 取表长度原语       
  local h = Metatable(op).__len  
  if h then    -- 用 *** 作数调用处理程序    
   return (h(op))  
   error(...)  
 end   nd 


"eq": == *** 作。 函数getcomphandler定义Lua如何选择比较 *** 作符的元方法。只有待比较的两个对象类型和选定 *** 作对应的元方法都相同,才会选择该元方法。 
function getcomphandler (op1,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
"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,"__eq")       
 if h then  
  return (h(op1,op2))       
  return false       
end
a ~= b等价于not (a == b)。

"lt": < *** 作。 
function lt_event (op1,255); Font-family:CourIEr"> if type(op1) == "number" and type(op2) == "number" then  
  return op1 < op2   -- 数字比较       
 elseif type(op1) == "string" and type(op2) == "string" then  
  return op1 < op2   -- 词典顺序比较       
  local h = getcomphandler(op1,"__lt")  
   error(...);  
 end    
end
a > b等价于b < a。

"le": <= *** 作。 
function le_event (op1,255); Font-family:CourIEr">  return op1 <= op2   -- 数字比较       
  return op1 <= op2   -- 词典顺序比较       

   h = getcomphandler(op1,"__lt")    
   if h then      
    return not h(op2,op1)    
   else      
    error(...);    
   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 
  if h == nil then    
 if type(h) == "function" then  
  return (h(table,key))     -- 调用处理程序       
  return h[key]    -- 对它重复上述 *** 作       

"newindex": 索引赋值table[key] = value。 

function settable_event (table,key,value)       
   rawset(table,value); 
   return 
  h = Metatable(table).__newindex  
  return 
  h(table,value)    -- 调用处理程序       
  h[key] = value      -- 对它重复上述 *** 作       

"call": 当Lua调用值时被调用。 

function function_event (func,...)       
 if type(func) == "function" then  
  return func(...)   -- 调用原语       
  local h = Metatable(func).__call  
   return h(func,...)  
 nd 

总结

以上是内存溢出为你收集整理的Lua中的元表和元方法全部内容,希望文章能够帮你解决Lua中的元表和元方法所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1246457.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存