Lua - 如何将对象的函数作为参数传递给另一个函数

Ray*_*jax 11 lua callback

local a = {}
function a:test1(value)
    print(value)
end
local b = {}
function b:test2(v1, v2)
    v2(100);
end
b:test2(_, a.test1)
Run Code Online (Sandbox Code Playgroud)

不行.价值为零.我可以找到一个在匿名函数中进行封装的解决方案

b:test2(variable, function(value) a:test1(value) end)
Run Code Online (Sandbox Code Playgroud)

但我发现它很糟糕

什么是正确的语法?

Rya*_*ein 10

anotherObject:aFunction(variable, object.doStuff) 是正确的语法.

使用:带函数的冒号只是一个带有隐式self参数作为第一个参数的调用或声明的语法糖.如果您想以更干净的方式遵循您在示例中显示的模式,则可以使用辅助函数.

local function bind(t, k)
    return function(...) return t[k](t, ...) end
end
Run Code Online (Sandbox Code Playgroud)

然后你就这样应用它.

anotherObject:aFunction(variable, bind(object, 'doStuff'))
Run Code Online (Sandbox Code Playgroud)

编辑:我相信你的问题的解决方案将需要在某种程度上绑定,而不需要修改Lua解释器或使用代码转换步骤.这基本上是因为Lua中的函数不包含有关其来源的任何信息.,表本身并不拥有它们存储的功能.

例如,以下是完全合法的Lua代码.

function Circle:area() -- function Circle.area(self)
    -- ...
end

-- Evaluate the function in the "area" slot with Square as the self parameter.
Circle.area(Square)
Run Code Online (Sandbox Code Playgroud)

当然,您可以尝试范式转换,但如果您正在构建一个完整的应用程序,可能为时已晚,因为正如您所说的那样,函数被绑定到已被索引的表中.因此,我提出以下疯狂的解决方案.

local mt = {}

function mt:__index(k)
    local v = self._slots[k]

    if v == nil then
        -- Ascend the inheritance tree.

        -- This has to be done with rawget all the way up,
        -- otherwise inherited functions would be repeatedly bound.
        local p = self

        repeat
            p = rawget(p, '_parent')
            if not p then break end
            v = p._slots[k]
        until v
    end

    if type(v) == 'function' then
        -- Return a self-bound version of the function.
        return function(...) return v(self, ...) end
    end

    return v
end

function mt:__newindex(k, v)
    self._slots[k] = v
end

--- Demo & Tests ---

local function Object(parent)
    local o = setmetatable({_slots = {}}, mt)
    if parent then rawset(o, '_parent', parent) end
    return o
end

local o1 = Object()
local o2 = Object(o1)

assert(o1.abc == nil, 'o1.abc should be nil')
o1.abc = 3
assert(o1.abc == 3, 'o1.abc should be 3')
assert(o2.abc == 3, 'o2.abc should be 3, inherited from o1')
o2.abc = 7
assert(o2.abc == 7, 'o2.abc should be 7, overriding o1')
assert(o1.abc == 3, 'o1.abc should be 3, unaffected by o2 setter')

function o1:test(bar)
    return self.abc + bar
end

assert(type(o1.test) == 'function', 'o1.test should be a function')
assert(type(o2.test) == 'function', 'o2.test should be a function, inherited from o1')

assert(o1.test(5) == 8, 'o1.test(5) should return 3 + 5 = 8')
assert(o2.test(11) == 18, 'o2.test(11) should return 7 + 11 = 18')

function o2:test2(fn)
    return self.abc + fn(7)
end

assert(o2.test2(o1.test) == 17, 'o2.test2(o1.test) should return 7 + (3 + 7) = 17')

o2.test3 = o1._slots.test -- proper function copying
assert(o2.test3(11) == 18, 'o2.test3(5) should return 7 + 11 = 18')

o2.abc = nil
assert(o2.abc == 3, 'o2.abc should be 3 again, inherited from o1 after clearing')

o2.abc = false
assert(o2.abc == false, 'o2.abc should be false, __index needs to differentiate between nil and false')
Run Code Online (Sandbox Code Playgroud)

这个metatable将为您提供所需的内容,并使用继承和绑定函数进行引导.您只需确保要遵循此模式的所有表也遵循示例代码中显示的对象创建方法.

为了解释,以这种方式制作的每个表都有任何新的分配重定向到_slots子表中,并且任何新的检索都检查了_parent继承树.如果值的类型是a function,则它返回一个新的闭包,其原始self开始检查绑定到找到的函数.

显然,使用:冒号语法从其中一个对象调用函数将是一个愚蠢的想法,因为它会评估o.fn(o, o),这可能不是你想要的.另一个警告是,这些对象将函数复制到这些对象上将无法按预期工作.o1.newfn = o2.fno2绑定函数放入o1,然后将重新绑定o1.最终结果将是这样的o2.fn(o2, o1).您必须从_slots表中复制功能.


结论:尽管这个作品,我不会亲自推荐它,从长远来看,因为它可能会产生混淆使用Lua中如何与表,索引和功能的任何人,并有是开销.你可以通过记忆闭包来取消它,但我会把这个决定留给你.祝好运!