【问题标题】:Three dimensional table in LuaLua中的三维表
【发布时间】:2014-02-09 08:09:44
【问题描述】:

我需要最好的方法来存储像素的三维表。我需要做的是有多个 x,y 表(基本上是三维的),它是光栅多个具有透明度的二维像素图。你看我可以像这样轻松地创建两个维度:

pixels = {{},{}}
pixels[1][5] = "green" --just an example
print(pixels[1][5])

但是,我不能像在 Java 中那样做到这一点......

pixels = {{}, {}, {}}
pixels[1][4][3] = "red" -- [x][y][z]
print(pixels[1][4][3])

这是我想要的功能,但我通过这样做令人恶心地解决了这个问题......

pixels = {}
pixels["x23,y02,z05"] = "green"
print(pixels["x23,y02,z05"]")

我只是使用 string.sub 和 string.concat 来读取和设置表格...我真的希望示例 2 的功能能够正常工作,但是我知道它可能需要以不同的方式实现。

【问题讨论】:

    标签: arrays multidimensional-array lua lua-table


    【解决方案1】:

    基本上有两种方法可以解决这个问题

    自动表格

    自动表使用元表透明地生成子表,基本上在创建它之后您应该能够忘记它们。

    function newAutotable(dim)
        local MT = {};
        for i=1, dim do
            MT[i] = {__index = function(t, k)
                if i < dim then
                    t[k] = setmetatable({}, MT[i+1])
                    return t[k];
                end
            end}
        end
    
        return setmetatable({}, MT[1]);
    end
    
    -- Usage
    local at = newAutotable(3);
    print(at[0]) -- returns table
    print(at[0][1]) -- returns table
    print(at[0][1][2]) -- returns nil
    at[0][1][2] = 2;
    print(at[0][1][2]) -- returns value
    print(at[0][1][3][3]) -- error, because only 3 dimensions set
    

    它们不太好的地方在于它们生成了很多个表格——很明显。这是一些内存开销,并且每个深度级别都会增加执行时间。

    它们的优点在于它们的大小可以完全动态化。你甚至可以让它们无限深。尽管在您的用例中,这很可能没有必要,甚至可能是个坏主意。

    尽管这种结构非常适合非整数索引,例如,您可以使深度甚至依赖于“模板结构”,从而实现透明的动态配置表,但我有点跑题了……

    扁平化数组

    另一个变体是扁平数组。 user3125367 已经写过关于它们的文章,但我想对此进行扩展,因为这样可以更方便地完成并解释一些事情。

    在 CG 中通常展平多维数组是一个好主意,因为这样您就可以非常轻松地进行许多矩阵运算。就所需的处理时间而言,计算修改后的索引也相对便宜。但应该注意,虽然有点明显,但这种方法仅适用于数字键和预定义的矩阵大小。

    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; 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
    
    -- Usage
    local mdt = newMdArray(100, 100, 100);
    local v = mdt(1, 2, 3);
    mdt(1, 2, 3, v*.1);
    

    此代码取自我的另一个答案:dynamic tables or arrays

    它可能可以优化一点(例如在闭包中计算X*Y)但我想将原始代码粘贴到这里。无论如何,有了这个,您都可以通过使用普通数组索引轻松地处理扁平结构:

    for i=1, #mdt
        mdt[i] = (mdt[i] or 0)*.5
    end
    

    以及直接访问 3d 索引:

    mdt(12, 13, 14, 0)
    

    您还可以通过将__index 字段添加到元表或表格保存矩阵维度等来轻松修改函数以返回缺失键的默认值。

    【讨论】:

    • 感谢 Autotable!
    【解决方案2】:

    除了经典的“数组中数组”方案之外,您还可以使用 Lua 表内部结构的好处。如何? Lua 表只是一个键到值的映射,当你将它作为一个数组使用时,你可能会跳过一些键,这几乎不会花费任何成本。

    t = { }
    t[1] = "Hello"
    t[500000] = "World" -- does NOT allocate additional 499999 elements
    

    因此,如果您的数据稀疏(超过 50% 的 3d 点没有价值),您可能会从中受益:

    local n_x, n_y, n_z = 1920, 1080, 1000
    local n_xy = n_x * n_y
    
    function setValue(t, x, y, z, value)
        assert(x > 0 and x < n_x)
        assert(y > 0 and y < n_y)
        assert(z > 0 and z < n_z)
    
        t[((z-1) * n_xy) + ((y-1) * n_z) + x] = value
    end
    
    function getValue(t, x, y, z)
        assert(x > 0 and x < n_x)
        assert(y > 0 and y < n_y)
        assert(z > 0 and z < n_z)
    
        return t[((z-1) * n_xy) + ((y-1) * n_z) + x]
    end
    
    t = { }
    setValue(t, 1, 1, 1, "red")
    setValue(t, 1, 1, 2, "green")
    

    【讨论】:

      【解决方案3】:

      在您的第一个代码中:

      pixels = {{},{}}
      

      相当于:

      pixels = {}
      pixels[1] = {}
      pixels[2] = {}
      

      这里,pixels[1] 已经是一个表,这就是为什么你可以为pixels[1][5] 赋值。

      但是在你的第二个代码中:

      pixels = {{}, {}, {}}
      

      这里,pixels 仍然是一个二维数组(有 3 个元素)。相当于:

      pixels = {}
      pixels[1] = {}
      pixels[2] = {}
      pixels[3] = {}
      

      pixels[1] 是一个表,但pixels[1][4] 不是。你需要做的是给pixels[1][4]一个像这样的表构造函数:

      pixels = {{}, {}, {}}
      pixels[1][4] = {}   --initialize it to an empty table
      pixels[1][4][3] = "red"
      print(pixels[1][4][3])
      

      【讨论】:

      • 谢谢!每次我需要添加像素时,我是否必须初始化它?例如:for i = 1,50 do pixel[i][i] = {} pixels[i][i][i] = "red" end you know...我实际上会在 for 中创建一个 for 循环循环,但我每次都称该初始化正确吗?
      • @user3214283 在访问或分配其成员之前确保它已经是一个表。如果它已经是一张桌子,你不需要再做一次。
      猜你喜欢
      • 2021-07-03
      • 2017-05-10
      • 2011-08-07
      • 1970-01-01
      • 2017-12-08
      • 2021-12-13
      • 2012-07-30
      • 2012-07-13
      • 2012-12-02
      相关资源
      最近更新 更多