Lua函数引用可以用作表键吗?

5
一个Lua新手在这里。我能够将函数引用存储为Lua表中的键吗?与以下内容类似:
local fn = function() print('hello') end
local my_table = {}
my_table[fn] = 123

这似乎可以正常工作,但我不知道是否可以依赖函数引用的唯一性。Lua是否可以在超出范围后重用函数引用?这会创建任何问题或者因某些原因被认为是一种不良实践吗?

4个回答

6

是的。我在lua中遇到的最好的事情之一就是任何东西都可以作为引用

你在表格中使用key的方式没有问题。

来自 Lua PiL

Lua中的表既不是值也不是变量;它们是对象。您可以将表视为动态分配的对象;您的程序仅操作对它们的引用(或指针)。没有隐藏的副本或在幕后创建新表。

在您的示例中,您没有向函数传递任何参数,因此,在您的情况下,将函数作为引用在程序中将无用。另一方面,像这样:

fn1 = function(x) print(x) end
fn2 = function(x) print("bar") end
t[fn1] = "foo"
t[fn2] = "foo"
for i, v in pairs(t) do i(v) end

有其用途。

一旦父表在作用域内,Lua可以重用函数引用吗?

只要您的父表在作用域内,是的。由于表被创建和操作但不被复制,因此您的函数引用不会从表索引内存中过时。 我稍后试试后再编辑这个答案。

这会产生任何问题吗,还是因为某种原因被认为是不良实践?

这只是被认为是不良实践,因为熟悉其他语言(如C、Python等)的用户在阅读表格时往往会考虑数组。在中,您没有这样的担忧,程序将完美地运行。

我不知道是否可以依赖函数引用的唯一性。

为什么?


感谢您提供详细的答案!非常感激!我曾经怀疑函数引用的唯一性,因为有可能出现两个函数具有相同的主体,Lua 解释器可能会决定将其作为单个引用以节省内存,就像大多数语言中的空对象和相同字符串一样。话虽如此,在大多数项目中不应该出现两个具有相同主体的函数的情况。另一个原因是 Lua 是否可以使用相同的哈希码(通过打印函数或调用 tostring(fn) 看到的哈希码)来表示其他函数。但正如您所澄清的那样,这是不太可能的,除非它被垃圾回收。 - Vivek
即使使用相同的主体,只要为它们使用不同的“名称”,它们就会被分配新的引用。 - hjpotter92
2
@BackinaFlash,这并不是普遍的真实情况。这对于5.1官方解释器来说是正确的;但对于5.2来说则不是。我相信这取决于个别的实现。 - daurnimator

3

函数引用的唯一性取决于Lua版本。

Lua 5.2手册如是说:
函数定义可能不会创建新值;如果新函数与旧函数没有可观察的区别,则可以重复使用以前的某些值。

例:

-- 10 different function references on Lua 5.1
-- The same function reference for Lua 5.2
local j
for i = 1, 10 do
   print(function() print(j) end)
end

-- 10 different function references for any Lua version
for i = 1, 10 do
   print(function() print(i) end)
end

因此,规则是:创建不同的闭包以获得不同的引用。

谢谢Egor!这是一个重要的知识点。不过我不希望在我的项目中出现两个具有相同函数体的函数,所以这应该不会有影响。 - Vivek
此外,如果我们在不同的作用域中创建具有相同主体的两个函数,例如作为不同表中的值,则它们会获得唯一的引用,从而解决了可能存在的相同主体函数的问题。 - Vivek

1
你可以这样做,function会一直保留在内存中,直到被垃圾回收器销毁。
你可以在交互式的lua中尝试:
> fn = function() print('hello') end
> print(fn)
function: 0x9d058d0

> fn2 = function() print('hello') end
> print(fn2)
function: 0x9d05ee8

> fn2=fn
> print(fn2)
function: 0x9d058d0

> print(function() print('hello') end)
function: 0x9d068a8

> print(function() print('hello') end)
function: 0x9d06bf8

记住,如果fnlocal,那么当它超出范围时将被垃圾回收。在您的情况下,这不应该是一个问题,因为my_table在同一块中是local的。请参见Lua文档关于局部变量的说明

在Luajit中打印nil?! - paulm

1
这似乎可以正常工作,但我不知道是否可以依赖函数引用的唯一性。
每个function() end语句都会创建一个新的闭包。如果它们来自同一个构造函数,则实际上函数的代码将被重复使用。
for i=1,100 do
    t[function() print(i) end] = i -- this function body will be reused
end

但每个闭包都是独一无二的,这对您的参考非常重要。

Lua是否可以在超出范围后重用函数引用?

只要fn作为my_table中的键使用,Lua就不会收集它。如果my_table也超出范围,以至于表和函数都被收集,那么Lua重用引用不会影响您。所以无论如何,您都是好的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接