Lua5.1中的_调用元方法实际上是如何工作的?
作为练习,我正在尝试在Lua中实现一个集合。具体地说,我希望采用Pil2 11.5的简单集实现,并将其扩展到包括插入值、删除值等功能 现在最明显的方法是: 正如预期的那样,我把数字1到6打印出来了。但是那些对Lua5.1中的_调用元方法实际上是如何工作的?,lua,call,Lua,Call,作为练习,我正在尝试在Lua中实现一个集合。具体地说,我希望采用Pil2 11.5的简单集实现,并将其扩展到包括插入值、删除值等功能 现在最明显的方法是: 正如预期的那样,我把数字1到6打印出来了。但是那些对Set.insert(s,value)的调用真的很难看。我更希望能够调用类似于ts:insert(value)的东西 我第一次尝试解决这个问题的方法是这样的: Set = {} function Set.new(l) local s = { insert = func
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,2,3,4,5}
ts:insert(5)
ts:insert(6)
for k in pairs(ts) do
print(k)
end
在您看到结果之前,这基本上是正常的:
1
2
3
4
5
6
insert
很明显,正在显示作为集合表成员的insert函数。这不仅比原来的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,2,3,4,5}
ts:insert(5)
ts:insert(6)
for k in pairs(ts) do
print(k)
end
现在我阅读这段代码的方式是:
- 当我调用
时,ts:insert(5)
不存在以被调用的事实意味着insert
元表将被搜索ts
“\u调用”
元表的ts
键返回“\u call”
Set.call
- 现在,调用
时使用的名称是Set.call
,这会导致它返回insert
函数Set.insert
被调用Set.insert(ts,5)
lua: xasm.lua:26: attempt to call method 'insert' (a nil value)
stack traceback:
xasm.lua:26: in main chunk
[C]: ?
在这一点上,我被难住了。我完全不知道从这里到哪里去。我在这段代码上进行了一个小时的黑客攻击,不同程度的变异越来越令人绝望,但最终的结果是我没有任何有效的东西。在这一点上,我忽略了什么显而易见的事情?我修改了您的第一个版本,这个版本将提供我认为您需要的功能
Set = {}
function Set.new(l)
local s = {}
setmetatable(s, {__index=Set})
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,2,3,4,5}
ts:insert(5)
ts:insert(6)
for k in pairs(ts) do
print(k)
end
Set = {}
Set.__index = Set
function Set:new(collection)
local o = {}
for _, v in ipairs(collection) do
o[v] = true
end
setmetatable(o, self)
return o
end
function Set:insert(v)
self[v] = true
end
set = Set:new({1,2,3,4,5})
print(set[1]) --> true
print(set[10]) --> nil
set:insert(10)
print(set[10]) --> true
你说:
现在我阅读这段代码的方式是:
- 当我调用ts:insert(5)时,事实上insert并不 要调用的exist表示ts元表正在运行 要搜索“\uuu call”李>
- ts元表的“\uuuu call”键返回Set.call李>
- 现在调用Set.call时使用名称insert,该名称导致 它可以返回Set.insert函数李>
- 调用Set.insert(ts,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
- 如果在
对象中未直接找到ts
,Lua将在其元表中查找insert
\uu索引。
- 如果它在那里并且是一个表,Lua将在那里搜索
insert
- 如果它在那里并且是一个函数,它将使用原始表(
在本例中)和正在搜索的键(ts
)调用它insert
- 如果它不在那里,则视为
李>nil
- 如果它在那里并且是一个表,Lua将在那里搜索
\u索引
,因此实际上是在调用nil
值
这可以通过将\u index
指向某个表来解决,即Set
,如果要将方法存储在该表中
对于\u调用
,它用于将对象作为函数调用时。即:
Set = {}
function Set.new(l)
local s = {}
setmetatable(s, {__index=Set, __call=Set.call})
for _, v in ipairs(l) do
s[v] = true
end
return s
end
function Set.call(s, f)
-- Calls a function for every element in the set
for k in pairs(s) do
f(k)
end
end
function Set.insert(t, v)
t[v] = true
end
ts = Set.new {1,2,3,4,5}
ts:insert(5)
ts:insert(6)
ts(print) -- Calls getmetatable(ts).__call(ts, print),
-- which means Set.call(ts, print)
-- The way __call and __index are set,
-- this is equivalent to the line above
ts:call(print)
现在我阅读这段代码的方式是:
- 当我调用ts:insert(5)时,insert不存在以被调用这一事实意味着将在ts元表中搜索“\u call”
\u调用元方法:
Lua中的面向对象冒号调用如下:
ts:insert(5)
只是语法上的糖分
ts.insert(ts,5)
ts["insert"](ts,5)
这本身就是语法上的糖
ts.insert(ts,5)
ts["insert"](ts,5)
因此,对ts
执行的操作不是调用,而是索引(ts[“insert”]
的结果就是所调用的内容),它由\u index
元方法控制
\uuuu index
元方法可以是一个简单情况下的表,您希望索引“回退”到另一个表(请注意,被索引的是元表中的uuu索引键的值,而不是元表本身):
\uuu 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
有关元表的更多信息,请参阅。+1以实际解释原始代码的错误。顺便说一句,因为我将call(s,f)
函数放在Set
——即getmetatable(ts)。\uu index
——您还可以编写ts:call(打印)
。这基本上是Zecc的答案,带有更多中间示例代码。很可能是,但我被说服了。:)美好的谢谢你的指点和非常详细的回答。完整的答案,但对于像我这样的新手来说很容易理解。杰出的美丽的解释;我想知道如何实现JavaScript风格的行为,其中函数是哈希表的一个子类型(因此可以使用通常的table.key
语法访问属性)。(更广泛的背景是,我正在尝试创建类,我喜欢JavaScript的Car.staticProperty
表示法,但更喜欢Python风格的Car()
构造函数表示法,而不使用new
关键字。)这个答案告诉我,我可以简单地使用一个表并给它一个\u调用
元方法来实现同样的效果!谢谢只想说我绝对爱你的用户名