Lua 5.1中的__call元方法如何实际工作?
作为练习,我正在尝试在Lua中进行一组实现.具体来说,我想采用Pil2 11.5的简单集合实现,并将其扩展到包括插入值,删除值等的能力.
现在,显而易见的方法(以及工作方式)是这样的: Set = {} function Set.new(l) local s = {} for _,v in ipairs(l) do s[v] = true end return s end function Set.insert(s,v) s[v] = true end ts = Set.new {1,2,3,4,5} Set.insert(ts,5) Set.insert(ts,6) for k in pairs(ts) do print(k) end 正如预期的那样,我打印出数字1到6.但是那些对Set.insert(s,value)的调用真的很难看.我宁愿能够调用类似ts:insert(value)的东西. 我对此解决方案的第一次尝试看起来像这样: Set = {} function Set.new(l) local s = { insert = function(t,v) t[v] = true end } for _,v in ipairs(l) do s[v] = true end return s end ts = Set.new {1,5} ts:insert(5) ts:insert(6) for k in pairs(ts) do print(k) end 这很有效,直到你看到它的结果: 1 2 3 4 5 6 insert 很明显,正在显示插入函数,它是set表的成员.这不仅比原始的Set.insert(s,v)问题更加丑陋,它也容易出现一些严重问题(比如如果“insert”是某个人试图进入的有效密钥会发生什么?).是时候再次上书了.如果我尝试这样做会发生什么?: Set = {} function Set.new(l) local s = {} setmetatable(s,{__call = Set.call}) for _,v in ipairs(l) do s[v] = true end return s end function Set.call(f) return Set[f] end function Set.insert(t,v) t[v] = true end ts = Set.new {1,5} ts:insert(5) ts:insert(6) for k in pairs(ts) do print(k) end 现在,我正在阅读此代码的方式是: >当我调用ts:insert(5)时,不存在要调用的插入的事实意味着将要搜索ts metatable的“__call”. 真正发生的是这个: lua: xasm.lua:26: attempt to call method 'insert' (a nil value) stack traceback: xasm.lua:26: in main chunk [C]: ? 在这一点上,我很难过.我完全不知道从哪里开始.我在这段代码上乱砍了一个小时不断变化,但最终的结果是我什么都没有用.在这一点上我忽略了什么显而易见的事情? 解决方法
这是你的问题.调用表本身时(参见函数),查询__call元方法: local ts = {} local mt = {} function mt.__call(...) print("Table called!",...) end setmetatable(ts,mt) ts() --> prints "Table called!" ts(5) --> prints "Table called!" and 5 ts"String construct-call" --> prints "Table called!" and "String construct-call" Lua中面向对象的冒号调用如下: ts:insert(5) 仅仅是语法糖 ts.insert(ts,5) 这本身就是语法糖 ts["insert"](ts,5) 因此,对ts采取的操作不是调用,而是索引(ts [“insert”]的结果是所谓的),它由__index元方法控制. __index元方法可以是一个简单大小写的表,你希望索引“回退”到另一个表(请注意,它是metatable中__index键的索引,而不是metatable本身): local fallback = {example = 5} local mt = {__index = fallback} local ts = setmetatable({},mt) print(ts.example) --> prints 5 作为函数的__index元方法的工作方式类似于您使用Set.call所期望的签名,除了它传递在键之前被索引的表: local ff = {} local mt = {} function ff.example(...) print("Example called!",...) end function mt.__index(s,k) print("Indexing table named:",s.name) return ff[k] end local ts = {name = "Bob"} setmetatable(ts,mt) ts.example(5) --> prints "Indexing table named:" and "Bob",--> then on the next line "Example called!" and 5 有关元表的更多信息,请参阅the manual. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |