Lua中关于元表和元方法学习教程

移动开发 iOS
Lua中关于元表和元方法学习是本文要介绍的内容,主要来了解Lua中每个值都可具有元表。 元表是普通的Lua表,定义了原始值在某些特定操作下的行为。你可通过在值的原表中设置特定的字段来改变作用于该值的操作的某些行为特征。

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中描述。特别一提,要获取给定对象的元方法,我们使用表达式

  1. metatable(obj)[event] 

它应被解读为

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

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

"add": + 操作。

下面的getbinhandler函数定义Lua如何选择二元操作的处理程序。首先尝试***操作数,如果它的类型没有定义该操作的处理程序,则尝试第二操作数。

  1. function getbinhandler (op1, op2, event)  
  2.  return metatable(op1)[event] or metatable(op2)[event]       
  3. end 

通过应用该函数,op1 + op2的行为是

 

  1. function add_event (op1, op2)  
  2.  local o1, o2 = tonumber(op1), tonumber(op2)  
  3.  if o1 and o2 then  -- 两操作数都是数字  
  4.   return o1 + o2   -- ‘+’此处是‘add’的原语  
  5.  else  -- 至少一个操作数不是数字    
  6.   local h = getbinhandler(op1, op2, "__add")  
  7.   if h then    -- 用两个操作数调用处理程序  
  8.    return (h(op1, op2))    
  9.   else  -- 没有可用的处理程序:缺省行为  
  10.    error(...)  
  11.   end         
  12.  end  
  13. end 
  1. "sub": - 操作。 行为类似于“add”操作。   
  2. "mul": * 操作。 行为类似于“add”操作。   
  3. "div": / 操作。 行为类似于“add”操作。   
  4. "mod": % 操作。 行为类似于“add”操作。以o1 - floor(o1/o2)*o2为操作原语。   
  5. "pow": ^ (取幂)操作。 行为类似于“add”操作,以函数pow(来自C数学库)为操作原语。   
  6. "unm": 一元-操作。   
  7. function unm_event (op)  
  8.  local o = tonumber(op)  
  9.  if o then  -- 操作数是数字?  
  10.   return -o  -- ‘-’此处是‘unm’的原语  
  11.  else  -- 操作数不是数字  
  12.  -- 尝试由操作数取得处理程序。  
  13.   local h = metatable(op).__unm  
  14.   if h then-- 用操作数调用处理程序  
  15.    return (h(op))  
  16.   else  -- 没有可用的处理程序:缺省行为      
  17.    error(...)    
  18.   end  
  19.  end       
  20. end 

"concat": .. (连接)操作。

  1. function concat_event (op1, op2)  
  2.  if (type(op1) == "string" or type(op1) == "number") and   
  3.  (type(op2) == "string" or type(op2) == "number") then    
  4.   return op1 .. op2  -- 字符串连接原语         
  5.  else    
  6.   local h = getbinhandler(op1, op2, "__concat")    
  7.   if h then      
  8.    return (h(op1, op2))    
  9.   else      
  10.    error(...)    
  11.   end  
  12.  end       
  13. end    

"len": # 操作。

  1. function len_event (op)  
  2.  if type(op) == "string" then    
  3.   return strlen(op)  -- 取字符串长度原语         
  4.  elseif type(op) == "table" then    
  5.   return #op  -- 取表长度原语         
  6.  else    
  7.   local h = metatable(op).__len    
  8.   if h then    -- 用操作数调用处理程序      
  9.    return (h(op))    
  10.   else  -- 没有可用的处理程序:缺省行为      
  11.    error(...)    
  12.   end         
  13.  end       
  14. end   

"eq": == 操作。 函数getcomphandler定义Lua如何选择比较操作符的元方法。只有待比较的两个对象类型和选定操作对应的元方法都相同,才会选择该元方法。

  1. function getcomphandler (op1, op2, event)         
  2.  if type(op1) ~= type(op2) then return nil end         
  3.  local mm1 = metatable(op1)[event]         
  4.  local mm2 = metatable(op2)[event]         
  5.  if mm1 == mm2 then   
  6.   return mm1   
  7.  else   
  8.   return nil   
  9.  end       
  10. end  
  11. "eq"事件定义如下:   
  12. function eq_event (op1, op2)         
  13.  if type(op1) ~= type(op2) then  -- 类型不同?    
  14.   return false   -- 对象不同         
  15.  end         
  16.  if op1 == op2 then   -- 相等原语?    
  17.   return true   -- 对象相同         
  18.  end       -- 尝试元方法         
  19.  local h = getcomphandler(op1, op2, "__eq")         
  20.  if h then    
  21.   return (h(op1, op2))         
  22.  else    
  23.   return false         
  24.  end       
  25. end  
  26. a ~= b等价于not (a == b)。   
  27.  
  28. "lt": < 操作。   
  29. function lt_event (op1, op2)         
  30.  if type(op1) == "number" and type(op2) == "number" then    
  31.   return op1 < op2   -- 数字比较         
  32.  elseif type(op1) == "string" and type(op2) == "string" then    
  33.   return op1 < op2   -- 词典顺序比较         
  34.  else    
  35.   local h = getcomphandler(op1, op2, "__lt")    
  36.   if h then      
  37.    return (h(op1, op2))    
  38.   else      
  39.    error(...);    
  40.   end         
  41.  end      
  42. end  
  43. > b等价于b < a。   
  44.  
  45. "le": <= 操作。   
  46. function le_event (op1, op2)         
  47.  if type(op1) == "number" and type(op2) == "number" then    
  48.   return op1 <= op2   -- 数字比较         
  49.  elseif type(op1) == "string" and type(op2) == "string" then    
  50.   return op1 <= op2   -- 词典顺序比较         
  51.  else    
  52.   local h = getcomphandler(op1, op2, "__le")    
  53.   if h then      
  54.    return (h(op1, op2))    
  55.   else      
  56.    h = getcomphandler(op1, op2, "__lt")      
  57.    if h then        
  58.     return not h(op2, op1)      
  59.    else        
  60.     error(...);      
  61.    end    
  62.   end         
  63.  end       
  64. end 

a >= b等价于 b <= a。注意,假定a <= b等价于not (b < a),那么当没有“le”元方法时,Lua尝试“lt”。

  1. "index": 索引访问table[key]。   
  2. function gettable_event (table, key)         
  3.  local h         
  4.  if type(table) == "table" then    
  5.   local v = rawget(table, key)    
  6.   if v ~= nil then   
  7.    return v   
  8.   end    
  9.   h = metatable(table).__index    
  10.   if h == nil then   
  11.    return nil   
  12.   end         
  13.  else    
  14.   h = metatable(table).__index    
  15.   if h == nil then      
  16.    error(...);    
  17.   end         
  18.  end         
  19.  if type(h) == "function" then    
  20.   return (h(table, key))     -- 调用处理程序         
  21.  else   
  22.   return h[key]    -- 对它重复上述操作         
  23.  end       
  24. end  
  25.  
  26. "newindex": 索引赋值table[key] = value。   
  27. function settable_event (table, key, value)         
  28.  local h         
  29.  if type(table) == "table" then    
  30.   local v = rawget(table, key)    
  31.   if v ~= nil then   
  32.    rawset(table, key, value);   
  33.    return   
  34.   end    
  35.   h = metatable(table).__newindex    
  36.   if h == nil then   
  37.    rawset(table, key, value);   
  38.   return   
  39.   end         
  40.  else   
  41.   h = metatable(table).__newindex    
  42.   if h == nil then      
  43.    error(...);    
  44.   end         
  45.  end         
  46.  if type(h) == "function" then    
  47.   h(table, key,value)    -- 调用处理程序         
  48.  else   
  49.   h[key] = value      -- 对它重复上述操作         
  50.  end       
  51. end     
  52.  
  53. "call": 当Lua调用值时被调用。   
  54. function function_event (func, ...)         
  55.  if type(func) == "function" then    
  56.   return func(...)   -- 调用原语         
  57.  else    
  58.   local h = metatable(func).__call    
  59.   if h then      
  60.    return h(func, ...)    
  61.   else      
  62.    error(...)    
  63.   end         
  64.  end       
  65. end   

小结:Lua中关于元表元方法学习教程的内容介绍完了,希望通过本文的学习能对你有所帮助!

责任编辑:zhaolei 来源: 博客园
相关推荐

2011-08-31 15:41:38

Lua解释器

2011-08-23 16:37:05

Lua数学库

2011-08-24 17:09:35

LUA闭包函数

2011-08-23 15:34:56

Lua模式 匹配

2011-08-24 14:14:13

LUA环境 配置

2011-08-23 17:06:03

2011-08-24 13:27:07

Lua 游戏C接口脚本

2011-08-23 16:48:41

Lua 5.1API 函数

2011-08-23 13:54:10

LUA全局变量

2011-08-24 15:34:44

MinGWLua环境配置

2011-08-24 15:42:38

LUA源代码

2011-08-25 15:41:42

Lua源码

2011-08-24 11:08:09

Lua

2011-08-23 17:33:08

LuaMetatable

2011-08-25 17:01:50

LUA网游游戏

2011-08-25 16:20:33

Lua脚本变量

2011-07-15 13:49:30

C++友元函数友元类

2011-08-23 16:22:45

Lua 4.0函数

2022-07-04 23:24:17

元宇宙虚拟世界伦理问题

2021-08-23 10:14:20

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号