在Lua的三维表

时间:2014-01-20 08:27:02

标签: arrays multidimensional-array lua lua-table

我需要最好的方法来存储像素的三维表。我需要做的是有多个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的功能可以工作,但是我知道它可能需要以不同的方式实现。

3 个答案:

答案 0 :(得分:3)

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

自动表

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

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)

您还可以通过向metatable添加__index字段或者保存矩阵尺寸等来轻松修改函数以返回缺失键的默认值。

答案 1 :(得分:2)

在您的第一个代码中:

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])

答案 2 :(得分:2)

除了阵列中阵列中的经典&#39;数组&#39;方案,你可以使用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")