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

[SOLVED] [Lua] nested pairs() error

Started by VADemon, 21 October 2014 - 06:34 PM
VADemon #1
Posted 21 October 2014 - 08:34 PM
Hi,

I need help with a strange error within nested pairs()

Script description: A customizable crafting (stamper) turtle. To define crafting recipes I used the following construction:
craftingGrid = {-- main table containing all possible recipes
	compressedSawdust = { -- how to craft this item
		true,	 true,	true,	false, -- for slots 1-16, true when there must be an item in the slot, false when not
		true,	 false, true,	false,
		true,	 true,	true,	false,
		false,	 false,	false,	false
	}
}

This table is then used and modified by the "initCraftingGrid" function which fails on the second for-loop with in pairs(gridValue) do with the following error:
line 30: invalid key to 'next'

Any idea what's causing this? Honestly, looks to me like an internal error

Full code:
Spoiler
-- how many items to craft at once?
targetItemCount = 16
-- sleep time between inv checks
sleepTimeInventory = 2
-- which item from /craftingGrid/ to craft?
targetItemType = "compressedSawdust"

craftingGrid = {
	compressedSawdust = {
		true,	 true,	true,	false,
		true,	 false, true,	false,
		true,	 true,	true,	false,
		false,	 false,	false,	false
	}
}

craftingGrid_meta = {
	-- itemType = {
	--		resultSlot = number,
	-- }
}

function initCraftingGrid()
	for gridKey, gridValue in pairs(craftingGrid) do
		print(type(gridValue)) -- returns a table as it should
		local requiredCraftingSlots = 0
		-- that's the first slot where the items would go to
		local craftResultSlot
		
		-- line 30 throwing that error; it should iterate through table acquired by the previous pairs() function, e.g. craftingGrid[compressedSawdust]
		for slotKey, slotValue in pairs(gridValue) do -- line 30
			if slotValue then
				requiredCraftingSlots = requiredCraftingSlots + 1
			else
				if not craftResultSlot then
					craftResultSlot = slotKey
				end
				
				gridValue[ slotKey ] = nil	-- erase value from table
			end
		end
		
		if requiredCraftingSlots <= 9 then
			gridValue.craftingSlots = requiredCraftingSlots
		else
			print("Error: craftingGrid for [" .. gridKey .. "] requires more than 9 crafting slots!")
			print("Double-check this")
			error()
		end
		
		craftingGrid_meta[ gridKey ].resultSlot = craftResultSlot
		
		craftingGrid[ gridKey ] = gridValue -- apply changes to the global table from above
	end
end


function craftingGridSort( targetItem )
	local readyForCrafting = false
	
	for i, v in pairs( craftingGrid[ targetItem ] ) do
		
		-- item in slot /i/?
		if v then
		
			local currentSlotItems = turtle.getItemCount(i)
			
			-- return next slot or, if at the end, the first slot:
			local nextSlot = ( next(craftingGrid[ targetItem ], i) or next(craftingGrid[ targetItem ]) )
			
			if currentSlotItems > targetItemCount then
				turtle.select(i)
				turtle.transferTo(nextSlot, (currentSlotItems - targetItemCount))
			end
			
			if nextSlot == 1 then
				readyForCrafting = true
			end
		end
	end
	
	return readyForCrafting
end

function inputCraftingItems()
	-- fetch from craftingGrid table
	return turtle.suckUp()
end

function outputCraftedItems( targetItemType )
	-- same as above
	turtle.select( craftingGrid_meta[ targetItemType ].resultSlot )
	
	return turtle.dropDown()
end

-- runtime
initCraftingGrid()

while true do
	print("Iteration")
	turtle.select(1)
	
	while inputCraftingItems( targetItemType ) == false do
		-- no new input items
		sleep( sleepTimeInventory )
	end
	
	craftingGridSort( targetItemType )
	turtle.craft()
	
	while outputCraftedItems( targetItemType ) == false do
		print("Can't output crafted Items!")
		sleep(5)
	end
	
	sleep( sleepTimeInventory )
end
Temporary link for testing: http://pastebin.com/72Hwre1W

Version: ComputerCraft1.64

UPD: This nested pairs() construction doesn't cause any errors on Lua: Demo
Spoiler
craftingGrid = {
	compressedSawdust = {
		true,	 true,	true,	false,
		true,	 false, true,	false,
		true,	 true,	true,	false,
		false,	 false,	false,	false
	}
}

function initCraftingGrid()
	for gridKey, gridValue in pairs(craftingGrid) do
		print(type(gridValue))
		local requiredCraftingSlots = 0
		-- that's the first slot where the items would go to
		local craftResultSlot
		
		for slotKey, slotValue in pairs(gridValue) do
			if slotValue then
				requiredCraftingSlots = requiredCraftingSlots + 1
			else
				if not craftResultSlot then
					craftResultSlot = slotKey
				end
				
				gridValue[ slotKey ] = nil	-- erase value from table
			end
		end
	end
end
initCraftingGrid()
Edited on 21 October 2014 - 07:45 PM
com_kieffer #2
Posted 21 October 2014 - 09:27 PM
Here's a solution : http://www.computercraft.info/forums2/index.php?/topic/11043-invalid-key-to-next/

In short :

 gridValue[ slotKey ] = nil      -- erase value from table

You can't do that. You can't erase values from a table as you iterate through it.
VADemon #3
Posted 21 October 2014 - 09:44 PM
Thanks for the reply.
So it's either LuaJ or Lua 5.1, cause my code indeed works with plain Lua 5.2.

Workaround: instead directly editing the table that's being iterated through, either edit the original (global) table or create a temporary new one.
Edited on 21 October 2014 - 07:46 PM
Bomb Bloke #4
Posted 22 October 2014 - 01:59 AM
You ARE editing the original table. That's also the table you're iterating through.

Remember that variables that point to tables are holding pointers. If you do this:

a = {}
b = a

… then both a and b will point to the exact same table in memory.

Putting aside any problems wiping values in that original table may cause, you could always just change this:

                for slotKey, slotValue in pairs(gridValue) do
                        if slotValue then
                                requiredCraftingSlots = requiredCraftingSlots + 1
                        else
                                if not craftResultSlot then
                                        craftResultSlot = slotKey
                                end

                                gridValue[ slotKey ] = nil      -- erase value from table
                        end
                end

… to this:

                for i = 1, table.maxn(gridValue) do
                        if gridValue[i] then
                                requiredCraftingSlots = requiredCraftingSlots + 1
                        else
                                if not craftResultSlot then
                                        craftResultSlot = i
                                end

                                gridValue[i] = nil      -- erase value from table
                        end
                end
Edited on 22 October 2014 - 12:00 AM