This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
hbomb79's profile picture

unpack nil values

Started by hbomb79, 18 June 2016 - 07:25 AM
hbomb79 #1
Posted 18 June 2016 - 09:25 AM
When trying to unpack a table containing nil values using unpack I realised that this doesn't work as it will stop once the first nil value has been encountered, not dissimilar to ipairs.

Inline with the Lua documentation, I created my own implementation that handles nil values just how I need it to with the catch that the tables length must be supplied (n):

function unpack (t, i, n)
  i = i or 1
  if i > n then return end
  return t[i], unpack(t, i + 1, n) --# stack overflow occurs here
end

The issue is I am encountering stack overflows, unlike the built in `table.unpack`.

If I unpack `{nil, nil, "value"}` I get `{}`, with my custom unpack I get `{[3] = "value"}`. My goal is to create a function that does this without overflowing the stack when too many values are supplied.


For context: I need to be able to unpack nil values because I am using an argument resolution system that loops through each given argument (vararg).

The function is configured to take arguments x, y, and z. If I only want to define 'z', I can call it with `nil, nil, "value"` in normal Lua. I need this to be possible from a table as I am parsing the arguments through XML.
Bomb Bloke #2
Posted 18 June 2016 - 10:16 AM
table.maxn(myTable) gets you the highest numeric index in your table (regardless as to where the first nil index is); no need to supply "n".

I suspect you don't need to do any of this, because of the automatic "arg" table constructed when building a vararg function:

local function varArgFunction(...)
	print(arg.n)  --> 2
end

varArgFunction(nil, "fruit")

But if you really require it:

function unpack(tbl, strt, fnsh)
	if not strt then strt = 1 end
	if not fnsh then fnsh = table.maxn(tbl) end
	if strt > fnsh then return end

	local str, cnt = {}, 1
	for i = strt, fnsh do
		str[cnt] = "tbl[" .. i .. "]"
		cnt = cnt + 1
	end
	
	return loadstring("local tbl = ... return " .. table.concat(str, ","))(tbl)
end
Edited on 18 June 2016 - 08:21 AM
hbomb79 #3
Posted 18 June 2016 - 10:22 AM
I do need this because I am constructing the vararg dynamically, I cannot hard code the 'nil's into the function call; instead they need to be dynamically unpacked. If I don't use an unpack and pass just unpack the table normally I get an empty result (as stated in the 'for context' section)

I will work on implementing your answer, thanks for your help. I had not thought of using a solution like the one you have produced.

EDIT: Tested and it works great. Thanks for your help Bomb Bloke
Edited on 18 June 2016 - 08:47 AM
theoriginalbit #4
Posted 18 June 2016 - 02:45 PM
You and your loadstring, its definitely not needed here.

Take the following code

function setCoords( ... )
  local coords = { ... }
  local x, y, z = unpack( coords )
  print("coords: ", tostring(x), tostring(y), tostring(z))
end

setCoords(1, nil, 5)
this would fail as per your issue Hbomb, x would be 1, but everything else would be nil.

However, unpack is amazing, and can take a start and end index for the unpack, combining this with table.maxn solves the issue

function setCoords( ... )
  local coords = { ... }
  local x, y, z = unpack( coords, 1, table.maxn(coords) )
  print("coords: ", tostring(x), tostring(y), tostring(z))
end

setCoords(1, nil, 5)

EDIT: it should also be noted that as per Bomb's other suggestion you can also use arg.n

function setCoords( ... )
  local x, y, z = unpack( arg, 1, arg.n )
  print("coords: ", tostring(x), tostring(y), tostring(z))
end

setCoords(1, nil, 5)
Edited on 18 June 2016 - 12:48 PM