I have a function that is meant to dynamically change the size of a 3D Array or table but it keeps breaking saying that it return value nil because it is out of bounds. Here is the code for it:
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
I know it's not very well commented but the idea is that it creates a new table with the changed dimensions, and then superimpose the data of blocks over the new table and finally overwriting blocks with the new temp table.
The length width and height are changed by one either incremented or decremented but only one at a time.
I'm not sure if I explained it clearly enough, if not let me know and I'll try explain in more detail.
Thanks all,
James
I think that the bug lies in your if statements. You should sizes of blocks against h, y and x, not height and length and width.
As a side note, do not use table.insert when you can substitute it with temp[h] = {}. It's faster. Also, try to use locals for temp storage.
Your specific error (probably)
You do not test against nil values. Any non-initialized table (= array in this case) members are nil
by definition. And comparing nil
with a number will generate an error:
lua: attempt to compare nil with number
However, as you seem unable to provide the actual error message, this is only a guess. Do not take me wrong these are errors in your code, but there could be something else wrong that I overlooked. Anyway here are some comments along with your code to show you what happens
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
You would have to test against nil on every level first, like
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
General programming mistakes
blocks
, length
, width
and height
seem to be parameters for your function but are not in its header, so I suppose you set them externally before calling the function? This is certainly not good practice.
temp
and num
should be declared local
.
Alternative
You can make the data-structure more intelligent, for example, if you put your 3D array in a flattened table and add a __call
metamethod like this
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
Then this is all you have to do to make a resized copy of it:
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
A nice bonus is, since this data structure flattens the 3D array into a 1D array, if you only want to copy the data, you can do so by simply accessing it as a table and copying each element:
for i=1, X*Y*Z do
new[i] = old[i]
end
Of course you could do the very same with a "real" (hidden) 3-D array in the background saving you the arithmetic calculations, however then you would have to test for empty values all the time to prevent nil errors.
Well I'm not sure if this is the best way to do it, but it does work.
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