我可以将一个函数注入到 Lua 函数中吗?

如果我有这个 lua 函数:

function dostuff(x)
   function foo () x=x+1; end
   foo();
   return x;
end

结果是我需要多次使用 foo。但它主要有用是因为它可以访问 x upvalue。有没有一种方法将 foo 移出 dostuff,但仍然让它访问 x?

我尝试将 foo() 全局化并添加到 dostuff 中,像这样:

function foo () x=x+1; end
function dostuff(x)
   foo();
   return x;
end
dostuff.foo = foo

但由于在 lua 中函数与表不同(与 js 不同),因此此方法无效。我仍然觉得这在 lua 中可以实现,可能需要使用元表。但我对此了解不够。我知道有很多方法可以避免这种情况并绕过它。我只是好奇是否有一种方法可以做到这一点。

也许另一种方法是,您可以使用闭包选择调用全局函数。

点赞
用户1161045
用户1161045

一个更清晰,更干净的方式是只将参数传递给foo,我建议你使用这种方法。

另一种方法是使用全局变量或局部变量(即模块或块内)对所有相关函数进行公用。

否则,我没找到任何其他的方法。我建议重新设计你的方法。

2013-06-18 06:15:56
用户1244588
用户1244588

由于您只提供了一个粗略的例子而没有实际使用情况,因此建议替代方案有些困难。首先,对我而言,这就像您想要做的事情:

local x;
function foo() x=x+1 end
function dostuff(a)
    x = a;
    foo();
    return x;
end

我的问题在于,foo非常简单,没有理由不将其作为带有参数 x 和返回值 x+1 的函数。此外,虽然 x 的必要初始化步骤不是很糟糕,而且不会立即导致任何错误,但是当您必须为另一个函数初始化变量时,它会使开发有些奇怪,并且如果您不这样做可能会创建一个调试地狱。

因此,由于 Lua 还支持多返回值,上面的代码实际上是一个坏点子,不会给您带来任何好处:

function foo(a,b,c) return a,b,c end
function dostuff()
    a,b,c = foo(a,b,c);
end

由于您写了关于代码重用的内容,这里提供了一个一般性的技巧,即利用闭包:

您可以调整结构,使 foo 不接受 x 为参数,而是使 dostuff 接受 foo 作为参数:

function dostuff(foo)
    return foo()
end
dostuff(function() return 1 end)
dostuff(function() return 2 end)

进一步推广会带您到一种称为 partial application 的技术:

function dostuff(foo)
    return function(x)
        return foo(x);
    end
end
dostuff(function(x) return x+1 end)(17)
local f = dostuff(function(x) return x+1 end)
f(17)f(18)f(19) -- ...

现在,这意味着您不仅可以从外部修改函数内部的工作方式,还可以保存该状态。作为额外的奖励,还可以将一次“实例”中只需执行一次的昂贵操作放在外部函数中,并节省一些性能。

希望这些想法足以帮助您解决代码重用的问题 ;)

2013-06-20 00:51:36
用户783743
用户783743

你想要的是动态作用域。不幸的是,Lua 不支持动态作用域。它支持的是词法作用域。在 JavaScript 中,你可以使用 eval 模拟动态作用域,但 Lua 没有 eval

最后的方法是使用继承。我通常在 Lua 中使用一个名为 extend 的函数来实现继承,它类似于 JavaScript 中的 Object.create 函数:

local o = {}

function o:extend(table)
    return setmetatable(table, {
        __index = self
    })
end

使用这种方法,我现在可以创建一个对象来实现动态作用域:

local dynamic = o:extend {}

function dynamic:foo()
    self.x = self.x + 1
end

方法 foo 是动态作用域的,因为它的变量 x 没有指向任何特定的值。它取决于 self 的值,而 self 的值是可以改变的。我们可以这样使用它:

function dostuff(x)
    local scope = dynamic:extend {
        x = x
    }

    scope:foo()

    return scope.x
end

但是,与其每次执行 dostuff 都创建一个新的 scope,不如简单地这样做:

local myscope = dynamic:extend {}

function myscope:dostuff(x)
    self.x = x
    self:foo()
    return self.x
end

实际上,如果您决定像上面所示那样重构代码,甚至不需要继承。你所需要做的就是:

local myscope = {}

function myscope:foo()
    self.x = self.x + 1
end

function myscope:dostuff(x)
    self.x = x
    self:foo()
    return self.x
end

唯一的区别是现在你需要调用 myscope:dostuff 而不是直接调用 dostuff。但这是一件好事,因为你不会污染全局作用域。

这是我会做的方式,也是我建议你采用的方式。所有 Lua 标准库函数也都是在对象上定义的。

2013-06-20 02:44:22