Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Lua Tables

edited July 2012 in General Posts: 563

I have done a brief overview of Lua tables at http://codeatuts.blogspot.com.au/2012/07/interlude-6-lua-tables.html

Let me know if I have missed anything or got anything wrong!

Tagged:

Comments

  • Posts: 2,161

    You've mixed the array part and the table part. Lua arrays have an array part indexed by integers starting at 1 and a table part indexed by arbitrary things (possibly strings). In particular, the ipairs primitive only acts on the array part and won't return all the key-value pairs. For that, you need pairs. Note that pairs returns stuff in effectively an arbitrary order but ipairs returns the array part in the right order.

    Another part where this has an effect is with the "convention" that lua arrays start at 1. The ipairs iterator will not see an entry with key 0. It starts at 1 and proceeds until it finds a nil value (so the array part should not have gaps to work correctly). I think that the length operator also only returns the length of the array part.

    When you create a table with no keys, as in myTable = {"a","b"} then the keys start at 1 and increment so this is the same as myTable = {1 = "a", 2 = "b"}.

  • edited July 2012 Posts: 146

    Hi,

    I noticed a few things, if you don't mind.

    For one, you use the terms table and array interchangeably in the first two parts. A table is always a table, that is, a hashed data structure. We will keep implementation details aside here ;) What you are referring to as arrays is actually using tables with array semantics. That is supported to a large degree, but, as you pointed out, things like the # operator depend on your "array" not having nils in them.

    Basically, what # returns is not the length of the table or the index of the last element, but the numeric index of an element such that, for a given table T, T[#T] ~= nil and T[#T+1] == nil. With the sole exception of a table with no numeric keys, in which case the above condition does not hold for the returned value of 0. But, for a table

    T = { [1] = 1, [3] = 2, [5] = 3 }
    

    the value of #T may be any of 1, 3 or 5.

    Also, table keys that are valid identifiers may always be accessed using dot notation, regardless of how they were initialized. So, if you do any of these:

    a = {} a.x = 1
    a = {} a['x'] = 1
    idx = 'x'  a = {} a[idx] = 1
    a = { ['x'] = 1 }
    idx = 'x' a = { [idx] = 1 }
    a = { x = 1 }
    

    a.x always equals a['x'], both are 1 in this case.

    Finally, as tables are always hashed data structures (from the user's point of view), if you cover ipairs(), I think you should also cover pairs().

    HTH,

    Gunnar

    Edit: as @Andrew_Stacey noted, tables can be indexed by anything that can be assigned to a variable in lua, even other tables, functions or userdata.

  • edited July 2012 Posts: 1,325

    Hi All,

    You guys are definitely the ones to ask my questions to.

    First, I'm a little puzzled. Some of the literature gave me the impression that arrays were not used in Lua. All you have are tables - which you can configure to behave like arrays. Is that true?

    Now to my problem. I have an array which I want to grow or reduce dependent on input from the user and am currently using the

    for k,v in pairs(table) do.

    construct. This doesn't seem to have control of the array size. For instance if you have a 15 by 15 table and you grow it to 30 by 30 the loop iterates up to the table max even if you reduce your working table back to 15 by 15.

    In other languages you can clear an array, re-initialize so that you can rebuild it to the new dimensions. Or, you can delete the array and re-build it from scratch after each size change.

    I'm looking at for loops to do this but wondered what other options there are available within Lua.

    Bri_G

    :)

  • Posts: 2,161

    If your indices are sequential, use ipairs. That iterates until it finds a nil value so is independent of how big the array was. I suspect that the problem you're running into is to do with "garbage collection" in that the table will eventually shrink, but it won't do so straightaway as for all it knows you're going to make it bigger again so it may as well hold onto its memory until it knows what you're going to do.

  • Posts: 563

    Thanks @Andrew_Stacey and @gunnar_z. I'm new to Lua so these tutorials are as much for me as anyone else. I find the best way to learn is try to explain it to others. I will update the tutorial with your observations (and attribution of course).

    I didn't realise there was a pairs function! I will add this as well.

    Thanks again.

  • Posts: 146

    @Bri_G basically it is true that lua does not have arrays per se. If you have a table that has integral numeric indices starting from 1 and continuing without holes to some maximum value, then this table can be treated as if it was an array. The standard lua implementation (the one Codea uses) even has internal optimizations for this use case. In this case, the # operator returns the index of the last element, and ipairs will iterate over all key-value pairs in the "array". In this case it does not even matter if the table has additional, non-numeric keys, you can access those as well, but the part with the numeric keys will be treated as if it was an array.

    As @Andrew_Stacey pointed out, when using tables as arrays, with sequential numeric indices, you should be using ipairs instead of pairs. This will ensure that you only get the key-value pairs with numeric keys, and also they will be ordered. pairs will return all key-value pairs, and an order on the keys, even the numeric ones, is not guaranteed.

    Now, resizing arrays is not really catered for explicitely. You can recreate the entire shebang, which is probably the best, i.e. fastest, solution, or you could append values to individial tables when enlarging, or set the extraneous table entries to nil when shrinking a table.

    One thing to keep in mind about nil and tables: in lua, nil means the absence of a value. So if you set a table entry to nil, you effectively delete it. This means, if you have a table you want to use as an array, and you want to signal that some values within are not set, you need to use some value different from nil or you will lose the array properties of the table. A common pattern is to create an empty table for this, like so:

    NULL = {}
    t = {1, 2, NULL, 4, 5}
    -- ... for some i
    if t[i] ~= NULL then
      print("there is a value set at index "..i)
    end
    

    Every empty table created with {} is always unique, so this is a neat way of creating all sorts of special values.

  • Posts: 22

    Once I found very interesting source of information:
    Lua Performance Tips by Roberto Ierusalimschy

    http://www.lua.org/gems/sample.pdf

    Take a look at page 19 "About tables" or better read it whole ;)

  • edited July 2012 Posts: 146

    in fact, read the whole book. And while you're at it, also read "Programming in Lua" (2nd ed.), and of course the manual. The order is: Manual, manual, manual, PiL, manual, manual, Gems (except for performance tips), manual. Keep all three books around for an occasional browse. If you're really interested, read the source. While reading, code a lot, all sorts of things. Reread the manual. Code more. Re-Read PiL, you should have a much firmer grasp on things this time around. Code more. Now read the performance tips (but keep in mind the rules about optimization, especially the first). Code more. By now you should be able to write lua programs in lua (as opposed to mimicking other languages like JavaScript). This alone should make your code go faster :) Oh, and writing modules in C or embedding lua into a C or C++ application will also further your learning about the language.

    Or, ignore all of that and just learn by example and look up the things you can't figure out in the manual. Should work for most of the time ;)

  • Posts: 22

    I assume you followed the former path? :)

  • Posts: 146

    I started when lua 5.0 came out, so I've had a lot of time for it ;)

  • edited July 2012 Posts: 1,325

    Hi @gunnar_z and emsi,

    Thanks for the information and feedback. I'm half way thru Snuff (Terry Pratchett) and have 12 more books on the shelf to read, all fiction and most fantasy. Keeps me sane. I do read the references but the gap between expert and noob pops up and I lose my way with the script. I think it's because experts write the books and sooner or later they slot into expert mode, then lose noobs like me.

    The key to getting concepts across is to speak in the same language consistently, many authors don't.

    I don't like the way Lua uses tables. I tend to opt for building a specific array and addressing it directly. Lua seems too loose with it's memory allocation. I've posted my Flood-it code, unoptimised in the Flood-it thread. Like to hear your comments on the table usage there.

    Some of the optimisation recommendations in the Gems book seem illogical, like defining variables etc as local outside functions. Tho' that may be me misunderstanding what the author is recommending. Does it mean use functions within functions?

    Thanks,

    Bri_G

    :)

  • Posts: 146

    @Bri_G nope. This works because basically a lua file is evaluated as if it was the body of a function. This is even true for codea, each tab is evaluated as if it was a function, containing function definitions. Thus local works just fine on a "global" (i.e. Tab-wide) scope here.

  • Posts: 1,325

    Hi @gunnar_z,

    Thanks for that - weird. Still I'm determined to learn and understand Lua, and you told me what I needed to know in my kinda language.

    I suppose it's just like learning other languages, you've just got to ringfence each language to avoid confusion. Tables seem similar to JavaScript arrays to me, powerful but easy to abuse.

    Thanks again,

    Bri_G

    :)

  • Posts: 563

    OK - I have updated the Lua Table tute (http://codeatuts.blogspot.com.au/) based on your feedback. Most appreciated!

    If you get a chance have a peek and check that I haven't introduced any new errors.

    Based on my reading, while you can think of a table having an array part and a hash part, this is more a convenience than a structural reality. Is that correct?

  • edited July 2012 Posts: 146

    @Reefwing I wrote about internal optimizations, that is one of them: tables do in fact have both an array and a hash part. However, I don't think "normal" lua programmers should be bothered with this. It is an optimisation for the common use case of tables as arrays.

    Concerning your article, I am not quite happy with the claim that tables are objects because they can contain functions. C Structures can contain functions, but that does not make them objects (I assume you're talking objects in the (not quite so well defined) OOP sense of the word). Tables can be used as objects, just as they can be used as arrays. And sets, lists, bags, queues, stacks, ...

    Apart from that, looks good now :)

  • Posts: 563

    @gunnar_z - so tables do have both, that's interesting but as you say probably a moot point.

    I see your point re: objects. I was using the term loosely. "can be an object" would have been better wording.

  • Posts: 6

    Great tutorial @Reefwing, thanks to you and the other contributors at this post.
    Especially the explanation about the "array" part and the "hash" part helped me a lot.

  • Posts: 563

    Thanks @Rob10

Sign In or Register to comment.