Lua在遍历表时不会包含'nil'。

3

我有一个匿名的Lua函数:

an_func = function(x) return x == nil end

我将在一个包含4个值的表格上进行测试:{'None', nil, 1, '1'}。因此,我编写了以下代码:
for num, element in pairs({'None', nil, 1, '1'}) do
    print(num .. ': ' .. tostring(an_func(element)))
end

当我运行它时,我只得到了三行:
1: false
3: false
4: false

带有 true 值的行(对应于 nil 表元素)没有打印。

你能解释一下,为什么 Lua 打印了所有结果,但是没有打印 true?我该如何修复代码片段以输出所有 4 行?

P.S. 我是 Lua 的新手。


你已经打了[arrays]的标签,而且有几个回答和评论使用了"array"这个术语。这是令人困惑的,因为1)它可能指的是Lua实现的内部数据结构或2)暗示了其他编程环境中不存在的特征。这就是为什么官方文档定义了一个“带有序列的表”(http://www.lua.org/manual/5.3/manual.html#2.1)的概念。你的表构造器没有生成一个带有序列的表。 - Tom Blodget
4个回答

9
使用pairs()函数时,它会遍历表中的所有元素。nil表示什么都没有,因此不会遍历它。为什么要告诉你表里面没有任何东西?如果您有一个全新的表,在使用任何索引时它将返回nil。例如:print(({}).test) --> nil 如果您真的想获取nil值,可以使用一个虚拟值:
local NIL = setmetatable({},{__tostring=function() return "nil" end})

an_func = function(x) return x == NIL end

for num, element in pairs({'None', NIL, 1, '1'}) do
    print(num .. ': ' .. tostring(an_func(element)))
end

输出:

1: false
2: true
3: false
4: false

据我所了解,您已向所有带有NIL的表添加了元表(类似于Python或Java中的属性)。即使我没有将匿名函数赋值给元表中的__tostring,这对我也是有效的。创建__tostring的原因是什么? - VeLKerr
2
@VeLKert 实际上他创建了一个名为NIL的表,当进行tostring操作时返回'nil'。 - warspyking
在这个特定的例子中,他根本不需要元表,他只是这样做,以便如果您打印它,您将得到预期的值“nil”。 - warspyking
1
正如@warspyking所提到的,元表更多的是一个视觉效果。执行print(NIL)现在将输出_nil_而不是_table:21BA9BE8(或类似内容)。如果您从未打算打印它,那就没关系了。此外,“所有带有NIL的表”是指仅NIL吗?我刚刚设置了NIL的元表。当您将NIL放入另一个表中时,您只是放置一个引用。只有NIL将具有该元表。(除非您使用setmetatable等) - EinsteinK
1
#EinsteinK 我感觉他还没有学习或理解元表,毕竟他自称为“新手”。 - warspyking
是的,我意识到我应该解释一下元表,但是我忘了。 - EinsteinK

4
在 Lua 中,所有东西(函数、字符串、线程等)都是一个值,只有nil(和技术上的none/void)是唯一的例外。简单来说,nil代表了没有值。因此,在迭代表时,为什么Lua会说那里没有任何内容呢?如果这样做,它必须迭代您可以创建的每个虚拟可能的索引。
然而,看起来您正在使用一个数组。您技术上可以迭代它,假设您知道长度。我假设表中的最后一项不是nil,并且在初始长度带有空洞之后没有分配任何空洞,但这在您的情况下可能不一定正确,如果不是,则必须自己跟踪长度并使用它而不是#运算符:
for i = 1,#t do
    local v = t[i]
    print (v == nil)
end

1
你真的假设数组部分没有空洞,因为长度运算符的定义方式。只有当表是序列时,表t的长度才被定义{10, 20, nil, 40}不是一个序列,因为它有键4但没有键3。http://www.lua.org/manual/5.3/manual.html#3.4.7 - Rochet2
@Rocket2 在你的例子中,#运算符会给我4。我理解你的意思,但是#运算符的工作方式,我可以用这种方式。 - warspyking
你可以做任何想做的事情。但是无法保证你所做的在所有场景中都能按预期行事。(未定义行为)以下是一个示例,展示当以不同方式定义相同表时长度运算符具有不同的行为:https://repl.it/Bo97 - Rochet2
1
当涉及到数组中的空洞时,#运算符非常棘手。通常情况下,当您构造一个具有_x_元素且不编辑表格的表格时,_#table_应返回_x_。然而,Lua手册指出这取决于实现。如果您想要100%确定,您需要遍历所有键并确定最高值。 - EinsteinK
1
你的答案有一个简单的错误。nil是一个值。(它的类型是nil。) - Tom Blodget
显示剩余7条评论

3

1
您需要循环遍历所有要打印值的键。 可能的方法:
  1. 预先知道长度。(warspyking提到了这一点)
  2. 使用结束标记。
  3. 以某种方式计算长度(例如使用maxn)。
  4. 使用某些东西来表示nil值。(EinsteinK给出了一个例子)
每种方法都有好坏之处,以下是一些例子:
对于第一种方法,在所有情况下都需要预先知道长度,这可能很困难。 结束标记很方便,但需要您移动结束标记。 如果最后一个元素为nil,则无法计算长度。
计算长度的示例-假设最后一个元素不为nil。 请注意,lua 5.1具有table.maxn,您可以在此自定义maxn实现中使用它。
an_func = function(x) return x == nil end

function maxn(t)
    local n = 0
    for k,v in pairs(t) do
        if k%1 == 0 then
            n = k
        end
    end
    return n
end

local t = {'None', nil, 1, '1'}
for num = 1, maxn(t) do
    print(num .. ': ' .. tostring(an_func(t[num])))
end

使用结束标记的示例:
an_func = function(x) return x == nil end

local END = {}
local t = {'None', nil, 1, '1', END}

local num = 1
while t[num] ~= END do
    print(num .. ': ' .. tostring(an_func(t[num])))
    num = num+1
end

maxn已被弃用,我认为你不应该建议使用它。出于好奇,既然你只处理数组,为什么不使用ipairs呢?我感觉我错过了什么。 - warspyking
ipairs在遇到第一个nil值时就停止了,所以我不知道如何用它来替换任何循环 - 它会在打印第一个值后停止。 maxn本身就是一种工具,可以完成它应该做的工作。我猜我推荐table.maxn是因为如果它是用C或其他语言实现的话可能会更快。虽然它已经被移除了,嗯。 所有这些解决方案都适用于数组中的任何空洞 - 除了maxn假设最后一个元素永远不为空,但除此之外任何值都可以为空。 - Rochet2
哦,对不起,我脑抽了一下。但是我仍然不建议即使在5.1版本中使用ipairs,它会引入不兼容性,并且已经被弃用了。 - warspyking

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