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

"pairs" And "ipairs"

Started by DiabolusNeil, 31 October 2013 - 02:24 PM
DiabolusNeil #1
Posted 31 October 2013 - 03:24 PM
I'm just curious; this seems like a helpful tool, but I cannot find and specific tutorials for it.
Yevano #2
Posted 31 October 2013 - 03:45 PM
This might be helpful: http://www.lua.org/pil/7.1.html

pairs and ipairs are iterator functions. You use them in for loops like as in


for key, value in pairs(some_table) do
	print("some_table[" .. key .. "] = " .. tostring(value))
end

pairs is for unordered iteration of table indices. ipairs is for ordered array indices.
InputUsername #3
Posted 31 October 2013 - 03:49 PM
This might be helpful: http://www.lua.org/pil/7.1.html

pairs and ipairs are iterator functions. You use them in for loops like as in


for key, value in pairs(some_table) do
	print("some_table[" .. key .. "] = " .. tostring(value))
end

pairs is for unordered iteration of table indices. ipairs is for ordered array indices.

In other words, if you have a table like this:

{
  [1] = "some value",
  [2] = "some other value",
  [3] = "third value"
}
you'll want to use ipairs, as it follows the order of the keys (1-2-3).
AgentE382 #4
Posted 31 October 2013 - 04:17 PM
In practice, I never use ipairs because numeric for loops are faster. This does exactly the same thing as Yevano's code, and will be more familiar to you if you've programmed in languages with traditional arrays before:
for i = 1, #some_table do
    print("some_table[" .. i .. "] = " .. tostring(some_table[i]))
end
AfterLifeLochie #5
Posted 31 October 2013 - 04:32 PM
ipairs(t) returns three values: an iterator function, the table t, and 0, so that the construction will iterate over the pairs (1, t[1]), (2, t[2]), [etc], up to the first integer key absent from the table. ipairs will only iterate over numeric pairs in the table, and will ignore any other indexes or values.

pairs(t) returns three values: the next function, the table t, and nil, so that the construction will iterate over all key–value pairs of table t. The order in which the indices are enumerated is not specified, even for numeric indices, meaning that the order of insertion or even declaration may not apply to the iteration.

Be careful; do not mix terminology such as 'array' and 'table', tables are the correct label as Lua allocates the first numeric index as `1`, and not `0`, like other programming languages, and, while they behave as associative-arrays, they are globally referenced as tables, and in type(t) are considered as "table", or "metatable".
Luanub #6
Posted 01 November 2013 - 12:03 AM
It's also good to keep in mind that if you are planning on using Lua outside of CC that in newer versions of Lua ipairs has been depreciated so you need to use a numerical loop to iterate through the table. I stopped using ipairs ages ago for this reason(that and as AgentE382 stated the numerical loops are slightly faster).
Engineer #7
Posted 02 November 2013 - 05:35 AM
It's also good to keep in mind that if you are planning on using Lua outside of CC that in newer versions of Lua ipairs has been depreciated so you need to use a numerical loop to iterate through the table. I stopped using ipairs ages ago for this reason(that and as AgentE382 stated the numerical loops are slightly faster).
I thought ipairs never had been deprecated, even 5.2
But, with that attitude you might as well not use environments as well
DiabolusNeil #8
Posted 04 November 2013 - 04:26 PM
Thanks for the help, I now understand this particular subject.
Lyqyd #9
Posted 04 November 2013 - 05:31 PM
It's also good to keep in mind that if you are planning on using Lua outside of CC that in newer versions of Lua ipairs has been depreciated so you need to use a numerical loop to iterate through the table. I stopped using ipairs ages ago for this reason(that and as AgentE382 stated the numerical loops are slightly faster).

Usually, with a post this blatantly wrong, I would simply delete it. I think this misconception needs to be addressed, so I'll leave it.

ipairs is not deprecated. In Lua 5.2, the utility of ipairs was actually increased. Why on earth would they add an __ipairs metamethod if ipairs itself was being deprecated? This silly rumor needs to stop. ipairs is a very useful function, and there are no significant reasons not to use it. Speed certainly isn't an issue, since you're already running your code on an in-game computer using Lua. Make your code readable and use ipairs. It will help prevent accidental misindexing and other errors.
AgentE382 #10
Posted 04 November 2013 - 09:13 PM
Lyqyd's right. ipars deprecation is a myth. However, I simply don't use iterators at all (if I can help it). Testing based on long lists of integers, ipairs is about 20% faster than pairs, but numeric for is about 50% faster than ipairs. (Tested with standalone Lua 5.2.2)

I know that processing long lists of integers is highly unlikely in a CC environment, but I use Lua often outside of CC and I want the absolute best performance I can get.
Lyqyd #11
Posted 04 November 2013 - 09:39 PM
Yeah, I can see using numeric for loops over ipairs outside of ComputerCraft if speed really is of the essence.
ElvishJerricco #12
Posted 04 November 2013 - 10:51 PM
Lyqyd's right. ipars deprecation is a myth. However, I simply don't use iterators at all (if I can help it). Testing based on long lists of integers, ipairs is about 20% faster than pairs, but numeric for is about 50% faster than ipairs. (Tested with standalone Lua 5.2.2) I know that processing long lists of integers is highly unlikely in a CC environment, but I use Lua often outside of CC and I want the absolute best performance I can get.

Honestly, the impact of ipairs vs numeric should be near negligible compared to the performance impact of the body of the loop itself in almost any realistic case.
distantcam #13
Posted 04 November 2013 - 11:12 PM
This reminds me of the 2 rules of optimization.
  • First rule of program optimization: don't do it.
  • Second rule of program optimization (for experts only): don't do it yet.
If you're so worried about performance that you're willing to micro-optimize (and let's be honest, ipairs vs numeric is a micro-optimization unless the implementation of Lua is broken) then you should be looking for a different language like say C++.
AgentE382 #14
Posted 05 November 2013 - 10:22 AM
I do program in C++. I've also read the document on Lua optimization by Roberto Ierusalimschy, some of the stuff on the Lua-Users Wiki, and the Lua VM / Bytecode analysis by Kein-Hong Man, esq.

Certain things are just always faster than others, so I always write code that way. I do that for most of the stuff on the SpringRTS Lua Performance page, but not all of it.

Like I said, I'm not micro-optimizing. I'm auto-optimizing. Just as an exercise, I might micro-optimize some of my code and measure the speedup.

Anyway, this thread isn't about optimization. It's about table traversal. I was just sharing one more way to do it. Honestly, numeric for is never slower than the other two ways and almost always marginally faster. Knowing that, it just became a habitual optimization.
AfterLifeLochie #15
Posted 05 November 2013 - 03:12 PM
Please be aware that optimization techniques for C-Lua do not apply to LuaJ, as the LuaJVM is implemented in both an entirely different language, and in a different manner. Anything you read as 'Lua' is likely the C-Lua, and therefore is entirely irrelevant.

Optimization inside LuaJ is exceptionally easy - the only thing you have to do is not do dumb things. We're talking on a Minecraft server; is there really any point in attempting to save a few operations in JVM? It's really such a negligible performance change in the context, it's not worth the time writing the optimizations in the first place.

As an interesting fact, ipairs is slower than pairs. Again, please read my original input - pairs returns an iterator function (next), the table, and nil; ipairs returns a custom iterator to loop over numeric-only sequential values.


function pairs( _t )
	local typeT = type( _t )
	if typeT ~= "table" then
		error( "bad argument #1 to pairs (table expected, got "..typeT..")", 2 )
	end
	return next, _t, nil
end

function ipairs( _t )
	local typeT = type( _t )
	if typeT ~= "table" then
		error( "bad argument #1 to ipairs (table expected, got "..typeT..")", 2 )
	end
	return function( t, var )
		var = var + 1
		local value = t[var] 
		if value == nil then
			return
		end
		return var, value
	end, _t, 0
end
Symmetryc #16
Posted 05 November 2013 - 04:51 PM
What's the point in returning nil as the third value? Is there something I'm not getting? I would assume that even if you didn't, nil would still be assigned if something were to be set to the value of it.
Lyqyd #17
Posted 05 November 2013 - 08:00 PM
It would, yes. It's good to have it there to signify that the third return value is important, even if we are returning nil. I don't believe its presence or lack thereof will affect the generated bytecode either way.

See PIL 7.2 - The Semantics of the Generic for for more information.
ElvishJerricco #18
Posted 05 November 2013 - 11:20 PM
It would, yes. It's good to have it there to signify that the third return value is important, even if we are returning nil. I don't believe its presence or lack thereof will affect the generated bytecode either way.

See PIL 7.2 - The Semantics of the Generic for for more information.

I studied the Lua compiler and the VM quite a bit when I was building LASM and LuaLua. I'm not sure if the LuaJ compiler is a simple port of the C-Lua compiler, meaning it would work the same, but the C-Lua compiler is not smart. It does zero optimizations and minimal logic. It will see that another expression is going to be used, parse it, and load it to the stack. Even though it's nil, it will have an extra loadnil instruction and have a higher return count in the ret instruction. It's not significant at all, but it does allocate one unnecessary register in some scenarios. It won't have hardly any effect on performance, so I agree it's probably better this way for the sake of knowing there's one more return value, but the byte code is different.