A question about loops

Hi all, I’ve away from coding for just over a year and I’m now just starting to get back to it by brushing up on the basics to remind myself.  One thing I’ve never got a handle on though is the use of pairs and ipairs when iterating through tables.  I’ve done a bit of googling but no-one seems to explain it in a way that actually explains it.

So, the question is, what are they, and why would I use them over a standard for/next loop?

In Lua there are two types of tables: indexed by a number and indexed by a string or key.  Lets talk about the first type first.

Here you have an table like:

local numericTable = {} numericTable[1] = "Hello" numericTable[2] = "World" numericTable[3] = "Welcome" numericTable[4] = "to" numericTable[5] = "Corona SDK"

or you could have defined it as:

local numericTable = { "Hello", "World", "Welcome", "to", "CoronaSDK" }

Both are the same. Each value will have a numeric index ranging from 1 to 5 since there are five entries.

Most people would loop over this with a simple ‘for’ loop using the # operator to get the number of entries in the table:

for i = 1, #numericTable do      print( i, numericTable[i] ) end

This is great as long as the keys are actually in a numeric sequence. But lets say you deleted one of the items from the table or for some reason there is a hole in the table such as:

local numericTable = {} numericTable[1] = "Hello" numericTable[2] = "World" numericTable[3] = "Welcome" numericTable[10] = "to" numericTable[11] = "Corona SDK"

In this case, the # operator will return three since the table is not in sequential order. Here you can use ipairs to iterate over the entire table.

for i, v in ipairs( numericTable ) do     print(i, v) end

Both will now produce the same output. In this case i, is the index into the table, v holds the value of numericTable[i].

Now tables indexed with string values are not guaranteed to be in any order and you can’t use a for i = 1, #tablesize to loop over the table. Given this table:

local city = { name = "Key West", state = "Florida", postalCode = "33040", latitude="24.5", latitude = "-81.8" }

If you want to iterate over this you will use pairs():

for k, v in pairs( city ) do      print( k, v ) end

So for this run, k will print things like: “name”, “state”, “postalCode”, etc. v will hold the values of those entries: “Key West”, “Florida”, “33040” etc.

Hope this helps explain the difference.

As a side note, the variables i, k and v in the examples above are in effect “local” to the inside of the loop only. Once the loop finishes, the values that are held in i, k and v will return to any previous definition.

Rob

Hi.

pairs() and ipairs(), along with string.gmatch(), io.lines(), and lfs.dir() off the top of my head, are cases of what are called iterators. For something “that actually explains it”, I’d recommend chapter 7 of Programming in Lua. A coroutine-based example is also shown a bit further into the book as well as a C-based one along the lines of lfs.dir() toward the end.

ipairs() is more or less a convenience. A normal for i = 1, n do loop is really not much different apart from syntax and avoiding some of the call and iterator overhead. The sole exception I can name is that, in the presence of holes in an “array”, ipairs() will visit slots 1, 2, 3, etc. until it hits a nil , whereas with for i = 1, #t do you go until you hit some nil , but not necessarily the first one.

pairs(), on the other hand, allows you to iterate the structure of generic tables, with arrays as one possibility among many. It does so by using a function called next(), which is the only means Lua exposes for investigating the structure of a table. Using next() directly tends to be pretty cumbersome.

As an iterator, pairs() wraps up all the details and puts them in an easy-to-use, easy-to-remember form. This is what iterators strive for generally. Much like how you might put large frequently repeated blocks of instructions into utility functions to save yourself effort down the line, an iterator is a sort of loop utility. You’ll tend to make these for fairly structured patterns, as suggested by the opening list: pairs() for generic tables, ipairs() for indexed tables, string.gmatch() for pattern-filled strings, io.lines() for lines of text in a file, lfs.dir() for files in a directory.

For a few more examples, I posted some snippets of my own here, some time ago. (Apologies for all the broken GitHub links; I did a lot of submodule reorganization. All the code still exists, though, if interested.)

Guys, thank you both so much.

That really does clear things up for me and now I get it.

@Rob - your explanation in particular was incredibly easy to understand, I just wish everybody could write tutorials that were that easy to grasp (no offence intended StarCrunch!)

@Appletreeman make sure you got @StarCrunch’s correction on the ipairs() version. I said it would skip nil’s. It does not.

Rob

In Lua there are two types of tables: indexed by a number and indexed by a string or key.  Lets talk about the first type first.

Here you have an table like:

local numericTable = {} numericTable[1] = "Hello" numericTable[2] = "World" numericTable[3] = "Welcome" numericTable[4] = "to" numericTable[5] = "Corona SDK"

or you could have defined it as:

local numericTable = { "Hello", "World", "Welcome", "to", "CoronaSDK" }

Both are the same. Each value will have a numeric index ranging from 1 to 5 since there are five entries.

Most people would loop over this with a simple ‘for’ loop using the # operator to get the number of entries in the table:

for i = 1, #numericTable do      print( i, numericTable[i] ) end

This is great as long as the keys are actually in a numeric sequence. But lets say you deleted one of the items from the table or for some reason there is a hole in the table such as:

local numericTable = {} numericTable[1] = "Hello" numericTable[2] = "World" numericTable[3] = "Welcome" numericTable[10] = "to" numericTable[11] = "Corona SDK"

In this case, the # operator will return three since the table is not in sequential order. Here you can use ipairs to iterate over the entire table.

for i, v in ipairs( numericTable ) do     print(i, v) end

Both will now produce the same output. In this case i, is the index into the table, v holds the value of numericTable[i].

Now tables indexed with string values are not guaranteed to be in any order and you can’t use a for i = 1, #tablesize to loop over the table. Given this table:

local city = { name = "Key West", state = "Florida", postalCode = "33040", latitude="24.5", latitude = "-81.8" }

If you want to iterate over this you will use pairs():

for k, v in pairs( city ) do      print( k, v ) end

So for this run, k will print things like: “name”, “state”, “postalCode”, etc. v will hold the values of those entries: “Key West”, “Florida”, “33040” etc.

Hope this helps explain the difference.

As a side note, the variables i, k and v in the examples above are in effect “local” to the inside of the loop only. Once the loop finishes, the values that are held in i, k and v will return to any previous definition.

Rob

Hi.

pairs() and ipairs(), along with string.gmatch(), io.lines(), and lfs.dir() off the top of my head, are cases of what are called iterators. For something “that actually explains it”, I’d recommend chapter 7 of Programming in Lua. A coroutine-based example is also shown a bit further into the book as well as a C-based one along the lines of lfs.dir() toward the end.

ipairs() is more or less a convenience. A normal for i = 1, n do loop is really not much different apart from syntax and avoiding some of the call and iterator overhead. The sole exception I can name is that, in the presence of holes in an “array”, ipairs() will visit slots 1, 2, 3, etc. until it hits a nil , whereas with for i = 1, #t do you go until you hit some nil , but not necessarily the first one.

pairs(), on the other hand, allows you to iterate the structure of generic tables, with arrays as one possibility among many. It does so by using a function called next(), which is the only means Lua exposes for investigating the structure of a table. Using next() directly tends to be pretty cumbersome.

As an iterator, pairs() wraps up all the details and puts them in an easy-to-use, easy-to-remember form. This is what iterators strive for generally. Much like how you might put large frequently repeated blocks of instructions into utility functions to save yourself effort down the line, an iterator is a sort of loop utility. You’ll tend to make these for fairly structured patterns, as suggested by the opening list: pairs() for generic tables, ipairs() for indexed tables, string.gmatch() for pattern-filled strings, io.lines() for lines of text in a file, lfs.dir() for files in a directory.

For a few more examples, I posted some snippets of my own here, some time ago. (Apologies for all the broken GitHub links; I did a lot of submodule reorganization. All the code still exists, though, if interested.)

Guys, thank you both so much.

That really does clear things up for me and now I get it.

@Rob - your explanation in particular was incredibly easy to understand, I just wish everybody could write tutorials that were that easy to grasp (no offence intended StarCrunch!)

@Appletreeman make sure you got @StarCrunch’s correction on the ipairs() version. I said it would skip nil’s. It does not.

Rob