【问题标题】:dynamic tables or arrays动态表或数组
【发布时间】:2013-05-30 22:54:55
【问题描述】:

我有一个旨在动态更改 3D 数组或表格大小的函数,但它一直在说它返回值 nil,因为它超出了界限。这是它的代码:

function resize()       
    temp = { }
    for h=1, height do
        table.insert( temp , { } )
        for y=1, length do
            table.insert ( temp[h], { } )
            for x=1, width do
                num = 16                
                if #blocks <= height then
                    if #blocks[h] <= length then
                        if #blocks[h][y] <= width then
                            num = blocks[h][y][x]
                        end
                    end
                end         
                table.insert( temp[h][y] , num )
            end
        end
    end 
    blocks = temp
end

我知道它的评论不是很好,但想法是它创建一个具有更改维度的新表,然后将块的数据叠加在新表上,最后用新的临时表覆盖块。

长度宽度和高度改变一个,增加或减少,但一次只能改变一个。

我不确定我是否解释得足够清楚,如果没有让我知道,我会尝试更详细地解释。

谢谢大家, 詹姆斯

【问题讨论】:

  • 请添加确切的错误消息并在代码中标记该行
  • 我可能遗漏了一些东西,但为什么不直接从现有表中添加或删除元素,而不是移动所有数据呢?

标签: function lua lua-table


【解决方案1】:

我认为错误在于您的 if 语句。您应该根据 h、y 和 x 来确定块的大小,而不是高度、长度和宽度。

附带说明,当您可以将 table.insert 替换为 temp[h] = {} 时,请勿使用它。它更快。另外,尝试使用本地人进行临时存储。

【讨论】:

  • 谢谢,我不知道你也可以这样说,temp[h] = { },而高度,长度,宽度实际上是我刚刚命名的h,y,x他们喜欢这样,以便我更容易阅读。
  • @user1990152 - 不,它们不一样。h、y 和 x 对于从 1 增加到高度、宽度、长度的变量是局部的。我想,通过将#blocks 与高度进行比较,您总是将其与新高度进行比较。因此,当您扩大阵列时,您永远不会得到ifs。
【解决方案2】:

好吧,我不确定这是否是最好的方法,但它确实有效。

function resize()       
temp = { } -- temp table

-- inserting all the height levels
for h=1, height do table.insert( temp , { } ) end

-- inserting all the lengths
for h=1, height do
    for l=1, length do table.insert( temp[h], { } ) end
end

-- inserting all the width and defaulting them to 0
for h=1, height do
    for l=1, length do
        for w=1, width do table.insert( temp[h][l] , 0 ) end
    end
end

-- if the canvas size is increasing
if #blocks <= height then
    if #blocks[1] <= length then
        if #blocks[1][1] <= width then
            for h=1, #blocks do
                for l=1, #blocks[1] do
                    for w=1, #blocks[1][1] do
                        -- fill in data from blocks
                        temp[h][l][w] = blocks[h][l][w]
                    end
                end
            end
        end
    end
end

--if the canvas size is decreasing
if #blocks >= height then
    if #blocks[1] >= length then
        if #blocks[1][1] >= width then
            for h=1, #temp do
                for l=1, #temp[1] do
                    for w=1, #temp[1][1] do
                        -- fill in data from blocks but not the last value
                        temp[h][l][w] = blocks[h][l][w]
                    end
                end
            end
        end
    end
end

-- overwrite blocks with the new dimensions
blocks = temp

【讨论】:

    【解决方案3】:

    您的具体错误(可能)

    您不会针对 nil 值进行测试。根据定义,任何未初始化的表(在这种情况下为数组)成员都是nil。并且将nil 与数字进行比较会产生错误:

    lua: attempt to compare nil with number
    

    但是,由于您似乎无法提供 实际 错误消息,这只是一个猜测。不要误会我的意思,这些是您的代码中的错误,但我可能忽略了其他错误。无论如何,这里有一些 cmets 以及您的代码来向您展示发生了什么

    if #blocks <= height then              -- say #blocks is 3, height is 4
        if #blocks[h] <= length then       -- E: in iteration 4 blocks[h] is nil
            if #blocks[h][y] <= width then -- E: in it. 3,4 blocks[h][y] is nil
                num = blocks[h][y][x]
            end
        end
    end
    

    您必须首先在每个级别上针对 nil 进行测试,例如

    if blocks[h] and blocks[h][y] and blocks[h][y][x] and
           #blocks[h]<=height and #blocks[h][y]<=height and #blocks[h][y][x]<=height
        num = blocks[h][y][x]
    end
    

    一般编程错误

    blockslengthwidthheight似乎是参数为您的函数但不在其标题中,所以我想您在调用函数之前将它们设置在外部?这当然不是好的做法。

    tempnum 应声明为 local

    另类

    你可以让数据结构更智能,例如,如果你把你的 3D 数组放在一个扁平的表中,然后像这样添加一个__call 元方法

    function newMdArray(X, Y, Z)
        local MT = { __call = function(t, x, y, z, v)
            if x>X or y>Y or z>Z or x<1 or y<1 or z<1 then return nil end
            local k = x + X*(y-1) + X*Y*(z-1);
            if v ~= nil then t[k] = v; end
            return t[k]
        end };
        return setmetatable({}, MT);
    end
    

    那么你要做的就是制作一个调整大小的副本:

    function resizeMdArray(array, X, Y, Z)
        local r = newMdArray(X, Y, Z);
        for x=1, X do
            for y=1, Y do
                for z=1, Z do
                    r(x, y, z, array(x, y, z) or 16);
                end
            end
        end
        return r;
    end
    

    一个不错的好处是,由于此数据结构将 3D 数组扁平化为 1D 数组,如果您只想复制数据,只需将其作为表格访问并复制即可每个元素:

    for i=1, X*Y*Z do
        new[i] = old[i]
    end
    

    当然,您可以在后台使用“真实”(隐藏)3-D 数组执行相同操作,从而节省算术计算,但是您必须始终测试空值以防止出现零错误。

    【讨论】:

    • 嗯,这比我预期的要深入一点...我知道你用它做什么,它有效,但我得到了一个不那么复杂的修复。谢谢你
    • 哦,这是不久前的事了……但我想我没有看到你的答案。虽然我认为这个解决方案使用起来要简单得多,因为您不必在想要访问数据时“手动”测试nil。但这当然是你的决定。
    猜你喜欢
    • 1970-01-01
    • 2012-01-28
    • 2017-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-11
    • 1970-01-01
    • 2019-02-06
    相关资源
    最近更新 更多