Lua 5.1中的__call元方法如何实际工作?

前端之家收集整理的这篇文章主要介绍了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”.
> ts Metatable的“__call”键返回Set.call.
>现在使用名称insert调用Set.call,使其返回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]: ?

在这一点上,我很难过.我完全不知道从哪里开始.我在这段代码上乱砍了一个小时不断变化,但最终的结果是我什么都没有用.在这一点上我忽略了什么显而易见的事情?

解决方法

Now the way I’m reading this code is:

  • When I call ts:insert(5),the fact that insert doesn’t exist to be called means that the ts Metatable is going to be searched for “__call”.

这是你的问题.调用表本身时(参见函数),查询__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.

猜你在找的Lua相关文章