Log In  


Hello!

I have just started to dive into Pico 8 and Lua. I've been working on building a simple animator for my game and have run into some strange issues with regard to tables.

I declare a table and later add to it like this:

a = {}
a.p = {x=1, y=2, z=3}

The function count(a) then returns 0. Additionally, all(a) and foreach(a, function) will not iterate overt the table. Strangely, I am still able to access the information. For example,

print(a.p.x)

will correctly print the number 1. If I declare the table explicitly

a = {p={x=1,y=2,z=3}}

I have no issues. I also don't have issues if I use add() to insert new table values. However, I need to utilize the associative array functionality of the table and to add and remove values at runtime, so neither of these solutions is sufficient.

The best fix I have found is to use pairs() instead of all(). For some reason, pairs() works properly regardless of how I add values to the table. This solves my problem, but the fact that I have no idea why leads me to believe that I have some serious misunderstanding of how Lua tables and/or the Pico 8 tables api works. I'm curious as to whether others have encountered this issue and would appreciate some feedback as to where my misunderstanding may lie.

1


A number of functions that work on tables operate on consecutive numeric indices starting at 1. So a loop over all(tbl), for instance, will run on tbl[1], tbl[2], tbl[3], and so on until it encounters one that isn't defined. It won't skip missing numbers, so if tbl has keys 1, 2, 3, and 7, it'll only get the first three.


1

Yes, you're trying to treat a non-sequence table as a sequence.

https://pico-8.wikia.com/wiki/Tables#Sequences


VERY LATE EDIT: When I said below that it was deprecated, I think that was true, but later on it was revived as an official API function, so disregard that.


As an aside, I'm pretty sure count() is deprecated.

It's literally a function roughly like this, hidden behind the scenes:

function count(t)
  local i=0
  repeat i=i+1 until t[i]==nil
  return i-1
end

It's not exactly performant or robust.

If you want the count of a numerically-indexed array, use the # operator, which is fast, or if you want to count the elements of an arbitrarily-mapped table, use pairs() and count them yourself:

function tablesize(t)
  local c=0
  for k,v in pairs(t) do
    c+=1
  end
  return c
end

Thank you! I did not realize that the implementation of add() and all() rely on a sequence table, but that makes quite a bit of sense. Out of curiosity, how does the # operator work? I have been treating # and count() as interchangeable, and it seems that # relies on a sequence as well.


The operator # works by returning 0 if t[1]==nil, or by searching for an integer n such that t[n]!=nil and t[n+1]==nil.

If the integer indices in your table are continuous and start at 1, this will return the length of the array part. Otherwise, this may return the length of the array, but you will have surprises.


@samhocevar

Not exactly. The # operator returns a cached value for the sake of speed. Lua tries to keep it correct by recognizing things that would make it change, but you can definitely set up an array from 1 to 100 and then set array[50] to nil and #array will still say 100.

Here:

Sometimes it'll get it right if you set a bunch to nil, but not always. The only guaranteed change is when you set a[#a] to nil.

Mostly you should consider the # operator to return the index of the highest-known non-nil entry.


Yep, the manual says this:

"When t is a sequence, #t returns its only border, which corresponds to the intuitive notion of the length of the sequence. When t is not a sequence, #t can return any of its borders. (The exact one depends on details of the internal representation of the table, which in turn can depend on how the table was populated and the memory addresses of its non-numeric keys.)"

A "border" here is what's been mentioned earlier in the thread: A non-nil value followed by a nil value.

I basically tread # as "undefined" for non-sequences.


@Felice maybe you misread me? I said Lua finds an integer that satisfies the property (any integer it deems suitable, and 100 satisfies the property in your example). And if the array is well-formed, then it returns the size of the array.


Just wanted to say thanks to everyone on this thread. I'm relatively new to Lua and didn't know the subtleties of working with sequences versus table/mappings. I'd still be banging my head on the desk trying to figure out why the heck all() and foreach() weren't working right if it weren't for y'all! ;-)


Count and # don't work in the newer versions.

Edit: I just found that the count functions are inclusive. So there aren't any problems.


@evman2k

Hmm? The # operator works fine in the current version.

Where have you had trouble? Perhaps you were expecting behavior other than what it does:

> table = { 1,2,4,8,16 }
> print( #table )
5
> _

The count() function also still works, but shouldn't generally be used because it literally counts the number of elements in the sequence iteratively, which is slow vs. the # operator which relies on the Lua interpreter's knowledge of the highest index in the sequence.


Sorry to dredge up an old post. @Felice (or whomever reads this), is there any results difference in # and count() outside of speed of operation ?

If so, could someone please post the difference between the two via code ? I'm always interested in learning new code.


@dw817

I've just done some testing with the currently-released version, and as far as I can tell, count() no longer iterates over the sequence portion of the table to see how many elements are there contiguously from index 1 onwards, which is what it used to do. It seems that now it just returns the same value that the # operator does, probably for the sake of performance gains on the host processor.

This is probably for the best, though at this point it might as well be deprecated, just like the bitwise functions that now have faster operator versions (e.g. lshr() deprecated for >>>).


Addendum: I see, the focus of count() is no longer that of finding out how long the table is, but rather of finding out how many times a specified value is in a table. If the value isn't specified, the result is #table.

Technically, this is how count is supposed to be used now, I think:

> t = {1,nil,2,nil,3,nil,4,nil,5}
> ?count(t)
9
> ?count(t, nil)
4
> ?count(t, 5)
1

Interesting. I kinda think this change should have been brought to our attention a little more overtly. It's rather useful.



[Please log in to post a comment]