【问题标题】:Lua unpack bug?Lua解包错误?
【发布时间】:2010-12-12 23:30:31
【问题描述】:

我在 Lua 解包函数中偶然发现了一个奇怪的行为

table1 = {true, nil, true, false, nil, true, nil}
table2 = {true, false, nil, false, nil, true, nil}

a1,b1,c1,d1,e1,f1,g1 = unpack( table1 )
print ("table1:",a1,b1,c1,d1,e1,f1,g1)

a2,b2,c2,d2,e2,f2,g2 = unpack( table2 )
print ("table2:",a2,b2,c2,d2,e2,f2,g2)

输出:

table1: true    nil true    false   nil nil nil
table2: true    false   nil nil nil nil nil

第二个解包将参数传递到第一个 nil 值。我可以忍受。 第一张桌子提供4?参数,中间一个为零。它有 4 个不是 nil 的参数,但它们不是显示的那个。

谁能解释一下? 这是用 codepad.org 和 lua 5.1 尝试过的

【问题讨论】:

    标签: lua unpack


    【解决方案1】:

    只需将开始和结束索引指定为unpack() 并使用table.maxn() 作为结束索引即可解决问题:

    表 1 = {真,无,真,假,无,真,无} a1,b1,c1,d1,e1,f1,g1 = unpack(table1, 1, table.maxn(table1) ) 打印 ("table1:",a1,b1,c1,d1,e1,f1,g1) -->table1: true nil true false nil true nil

    两个表的处理方式存在差异的真正原因在于确定表的数组部分长度的逻辑。

    luaB_unpack() 函数使用luaL_getn(),它是根据lua_objlen() 定义的,它为表调用luaH_getn()luaH_getn() 查看数组的最后一个位置,如果是nil,则对表中的边界执行二分查找(“这样 t[i] 为非 nil 且 t[i+1] 为零”)。对数组末尾的二进制搜索是table1table2 处理方式不同的原因。

    只有当数组中的最后一个条目是 nil 时,这才是问题。

    来自Programming in Lua(pg.16)(你应该买这本书。): 当一个数组有空洞——里面有 nil 元素——长度操作符可以假设这些 nil 元素中的任何一个作为结束标记。因此,您应该避免在可能包含空洞的数组上使用长度运算符。

    unpack() 使用长度运算符lua_objlen(),它“可能假定任何 [the] nil 元素作为数组的结尾”。

    【讨论】:

    • 谢谢你,你拯救了我的一天。这是否意味着 table.maxn() 会遍历表的整个分配大小?
    • table.maxn() "返回给定表的最大正数索引" 见lua.org/manual/5.1/manual.html#pdf-table.maxn
    • 是table.maxn真的保证在这里返回正确的值(=表字面量中的对象数)吗?
    • @kazer.se table.maxn 将返回 6,这是表 table1 的最大索引,具有非零条目。第7位的nil不会被table.maxn计算在内;但是,对于这种情况,它可以完美运行。 unpack 将返回 6 个元素,并且被分配的第 7 个变量将收到 nil,因为“列表已根据需要扩展了尽可能多的 nil”。
    【解决方案2】:

    2.2 - Values and Types

    [...] table 类型实现关联 数组,即可以是数组 不仅用数字索引,而且 任何值(nil 除外)。表 可以是异构的;也就是说,他们 可以包含所有类型的值 (零除外)。 [...]

    nil 赋予条目将破坏表枚举,并且您的变量将无法正确初始化。

    下面是一个简单的例子,演示了一个有问题的行为:

    table1 = {true, false, nil, false, nil, true, nil}
    for k,v in ipairs(table1) do
      print(k, v)
    end
    

    输出:

    1   true
    2   false
    >Exit code: 0
    

    【讨论】:

    • 更具体地说,我怀疑它忽略了他指定的nils。
    • 不,它不是 RCIX,在这种情况下,我猜表 2 也会返回 4 个值。
    • for 循环停止的唯一原因是迭代器在位置 3 返回了 nil。表仍然正确初始化,因为 assert(table1[1]==true and table1[2]==false and table1[3]==nil and table1[4]==false and table1[5]==nil and table1[6]==true and table1[7]==nil) 没有断言。
    • @gwell,我说过nil 可以打破枚举。我没有说元素将不复存在。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-26
    • 1970-01-01
    • 2017-08-08
    • 2015-06-02
    • 2014-10-14
    相关资源
    最近更新 更多