Lua Complex Tables/List Sizes - lua

I am trying to find the number of entries for test[0]
test = {}
test[0] = {}
test[0].x = {}
test[0].x[0] = 1
test[0].x[1] = 1
test[0].x[2] = 1
test[0].y = {}
test[0].y[0] = 1
I am expecting table.getn(test[0]) to be 2 for entries test[0].x and test[0].y but it results in 0. Why is this, and what do I need to do to get what I am looking for?

Note that table.getn in Lua 5.0 has been replaced by the # operator since Lua 5.1
The size of a table is only valid for the sequence part of a table (i.e, with positive numeric keys from 1 to some number n, and n is the size).
In this example, test[0] has only two kesy "x" and "y". As a result its size is 0.

table.getn and the lua 5.1 length operator are defined to operate on "lists" or arrays. Your table isn't one. It has no numerical indices.
So the result is undefined in lua 5.1 (though it will be zero here) and 0 in lua 5.0 as the size is defined to be one less the first integer index with a nil value which is the integer index 1.
Also worth noting is that table.getn(test[0].x) will return 2 and table.getn(test[0].y) will return 0 (since lua arrays start at 1).

Related

Casting a value in Lua table, not a link

I have simplified my code so you can have a better understanding:
x = {}
x["foo"]=1
a = {}
a[1]=x
x["foo"]=2
a[2]=x
print(a[1]["foo"])
print(a[2]["foo"])
The result is:
2
2
Or I was expecting:
1
2
I understant that a[1] is directing at the adress of the table x["foo"]. Then, when I change the value of this table, the variable a[1] points to the new value.
How can I tell Lua that I want to assign the VALUE and not link to and adress?
And just another thing: if x is a "simple" variable, not an array, the value is passed:
y = {}
x = 1
a = {}
a[1] = x
x = 2
a[2] = x
print(a[1])
print(a[2])
returns
1
2
The Lua manual, last but one paragraph of §2.1, says:
Tables, functions, threads, and (full) userdata values are objects: variables do not actually contain these values, only references to them. Assignment, parameter passing, and function returns always manipulate references to such values; these operations do not imply any kind of copy.

Non numeral indeces and the # never counts?

Given a table with mixed indexes like:
table = {
foo = 'bar'
[1] = 'foobar'
}
My question is about the # which gives the last index which is not separate through a gap while iterating through the table.
print(#table)
will give the output 1.
table = {
foo = 'bar',
lol = 'rofl',
[1] = 'some',
[2] = 'thing',
[3] = 'anything',
[4] = 'else'
}
print(#table)
should print 4
Can I be 100% sure that the # will never be distracted by non-numeral indexes?
Are those indexes really unregarded at every time?
Yes, you can count on that (in lua 5.1).
From the lua reference manual:
The length operator is denoted by the unary operator #. The length of
a string is its number of bytes (that is, the usual meaning of string
length when each character is one byte).
The length of a table t is defined to be any integer index n such that
t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil, n can be
zero. For a regular array, with non-nil values from 1 to a given n,
its length is exactly that n, the index of its last value. If the
array has "holes" (that is, nil values between other non-nil values),
then #t can be any of the indices that directly precedes a nil value
(that is, it may consider any such nil value as the end of the array).
lua 5.2 allows for the __len metamethod to operate on tables and that means # can do other things. See #kikito's answer for some examples.
Etan answer is correct, but not complete.
In Lua, if a table's metatable has a __len function, it will control what the # operator spits out. One can define it so that it takes into account the non-array keys.
local mt = {__len = function(tbl)
local len = 0
for _ in pairs(tbl) do len = len + 1 end
return len
end}
This demonstrates the thing:
local t = {1,2,3,4,foo='bar',baz='qux'}
print(#t) -- 4
setmetatable(t, mt)
print(#t) -- 6
If you really want to make sure that you get the "proper" array-like length, you must use rawlen instead:
print(rawlen(t)) -- 4, even with the metatable set
Edit: Note that __len does not work as I mention on Lua 5.1
The only way is to iterate through entries and count them. Iterate with ipair through the item and increment counter then return result.
function tablelength(T)
local count = 0 for _ in pairs(T) do
count = count + 1 end
return count
end
The # operator only work for hash table type.
See: How to get number of entries in a Lua table?

Why does Lua's length (#) operator return unexpected values?

Lua has the # operator to compute the "length" of a table being used as an array.
I checked this operator and I am surprised.
This is code, that I let run under Lua 5.2.3:
t = {};
t[0] = 1;
t[1] = 2;
print(#t); -- 1 aha lua counts from one
t[2] = 3;
print(#t); -- 2 tree values, but only two are count
t[4] = 3;
print(#t); -- 4 but 3 is mssing?
t[400] = 400;
t[401] = 401;
print(#t); -- still 4, now I am confused?
t2 = {10, 20, nil, 40}
print(#t2); -- 4 but documentations says this is not a sequence?
Can someone explain the rules?
About tables in general
(oh, can't you just give me an array)
In Lua, a table is the single general-purpose data structure. Table keys can be of any type, like number, string, boolean. Only nil keys aren't allowed.
Whether tables can or can't contain nil values is a surprisingly difficult question which I tried to answer in depth here. Let's just assume that setting t[k] = nil should be the observably the same as never setting k at all.
Table construction syntax (like t2 = {10, 20, nil, 40}) is a syntactic sugar for creating a table and then setting its values one by one (in this case: t2 = {}, t2[1] = 10, t2[2] = 20, t2[3] = nil, t2[4] = 40).
Tables as arrays
(oh, from this angle it really looks quite arrayish)
As tables are the only complex data structure in Lua, the language (for convenience) provides some ways for manipulating tables as if they were arrays.
Notably, this includes the length operator (#t) and many standard functions, like table.insert, table.remove, and more.
The behavior of the length operator (and, in consequence, the mentioned utility functions) is only defined for array-like tables with a particular set of keys, so-called sequences.
Quoting the Lua 5.2 Reference manual:
the length of a table t is only defined if the table is a sequence, that is, the set of its positive numeric keys is equal to {1..n} for some integer n
As a result, the behavior of calling #t on a table not being a sequence at that time, is undefined.
It means that any result could be expected, including 0, -1, or false, or an error being raised (unrealistic for the sake of backwards compatibility), or even Lua crashing (quite unrealistic).
Indirectly, this means that the behavior of utility functions that expect a sequence is undefined if called with a non-sequence.
Sequences and non-sequences
(it's really not obvious)
So far, we know that using the length operator on tables not being sequences is a bad idea. That means that we should either do that in programs that are written in a particular way, that guarantees that those tables will always be sequences in practice, or, in case we are provided with a table without any assumptions about their content, we should dynamically ensure they are indeed a sequence.
Let's practice. Remember: positive numeric keys have to be in the form {1..n}, e.g. {1}, {1, 2, 3}, {1, 2, 3, 4, 5}, etc.
t = {}
t[1] = 123
t[2] = "bar"
t[3] = 456
Sequence. Easy.
t = {}
t[1] = 123
t[2] = "bar"
t[3] = 456
t[5] = false
Not a sequence. {1, 2, 3, 5} is missing 4.
t = {}
t[1] = 123
t[2] = "bar"
t[3] = 456
t[4] = nil
t[5] = false
Not a sequence. nil values aren't considered part of the table, so again we're missing 4.
t = {}
t[1] = 123
t[2] = "bar"
t[3.14] = 456
t[4] = nil
t[5] = false
Not a sequence. 3.14 is positive, but isn't an integer.
t = {}
t[0] = "foo"
t[1] = 123
t[2] = "bar"
Sequence. 0 isn't counted for the length and utility functions will ignore it, but this is a valid sequence. The definition only gives requirements about positive number keys.
t = {}
t[-1] = "foo"
t[1] = 123
t[2] = "bar"
Sequence. Similar.
t = {}
t[1] = 123
t["bar"] = "foo"
t[2] = "bar"
t[false] = 1
t[3] = 0
Sequence. We don't care about non-numeric keys.
Diving into the implementation
(if you really have to know)
But what happens in C implementation of Lua when we call # on a non-sequence?
Background: Tables in Lua are internally divided into array part and hash part. That's an optimization. Lua tries to avoid allocating memory often, so it pre allocates for the next power of two. That's another optimization.
When the last item in the array part is nil, the result of # is the length of the shortest valid sequence found by binsearching the array part for the first nil-followed key.
When the last item in the array part is not nil AND the hash part is empty, the result of # is the physical length of the array part.
When the last item in the array part is not nil AND the hash part is NOT empty, the result of # is the length of the shortest valid sequence found by binsearching the hash part for for the first nil-followed key (that is such positive integer i that t[i] ~= nil and t[i+1] == nil), assuming that the array part is full of non-nils(!).
So the result of # is almost always the (desired) length of the shortest valid sequence, unless the last element in the array part representing a non-sequence is non-nil. Then, the result is bigger than desired.
Why is that? It seems like yet another optimization (for power-of-two sized arrays). The complexity of # on such tables is O(1), while other variants are O(log(n)).
In Lua only specially formed tables are considered an array. They are not really an array such as what one might consider as an array in the C language. The items are still in a hash table. But the keys are numeric and contiguous from 1 to N. Lua arrays are unit offset, not zero offset.
The bottom line is that if you do not know if the table you have formed meets the Lua criteria for an array then you must count up the items in the table to know the length of the table. That is the only way. Here is a function to do it:
function table_count(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
If you populate a table with the "insert" function used in the manner of the following example, then you will be guaranteed of making an "array" table.
s={}
table.insert(s,[whatever you want to store])
table.insert could be in a loop or called from other places in your code. The point is, if you put items in your table in this way then it will be an array table and you can use the # operator to know how many items are in the table, otherwise you have to count the items.

What does # mean in Lua?

I have seen the hash character '#' being added to the front of variables a lot in Lua.
What does it do?
EXAMPLE
-- sort AIs in currentlevel
table.sort(level.ais, function(a,b) return a.y < b.y end)
local curAIIndex = 1
local maxAIIndex = #level.ais
for i = 1,#currentLevel+maxAIIndex do
if level.ais[curAIIndex].y+sprites.monster:getHeight() < currentLevel[i].lowerY then
table.insert(currentLevel, i, level.ais[curAIIndex])
curAIIndex = curAIIndex + 1
if curAIIndex > maxAIIndex then
break
end
end
end
Apologies if this has already been asked, I've searched around on the internet a lot but I haven't seem to have found an answer. Thanks in advance!
That is the length operator:
The length operator is denoted by the unary operator #. The length of a string is its number of bytes (that is, the usual meaning of string length when each character is one byte).
The length of a table t is defined to be any integer index n such that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil, n can be zero. For a regular array, with non-nil values from 1 to a given n, its length is exactly that n, the index of its last value. If the array has "holes" (that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider any such nil value as the end of the array).
# is the lua length operator which works on strings or on table arrays
Examples:
print(#"abcdef") -- Prints 6
print(#{"a", "b", "c", 88}) -- Prints 4
-- Counting table elements is not suppoerted:
print(#{["a"]=1, ["b"]=9}) -- # Prints 0
#is most often used to get the range of a table. For example:
local users = {"Grace", "Peter", "Alice"}
local num_users = #users
print("There is a total of ".. num_users)
Output:
3

Might Lua's length operator return a negative index?

The, well, special specification of Lua's length operator made me wonder whether Lua would be "allowed" to return a negative value in a situation like
#{[-5]=1,[-1]=3}
It says:
The length of a table t is defined to be any integer index n such that t[n] is not nil and t[n+1] is nil;
n=-5 and n=-1 would meet this criterion in my example, right?
moreover, if t[1] is nil, n can be zero.
Right, it can be zero, but it's not guaranteed, right?
For a regular array, with non-nil values from 1 to a given n, its length is exactly that n, the index of its last value.
This isn't the case here, so it doesn't apply.
If the array has "holes" (that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider any such nil value as the end of the array).
This is the case here, so again, n=-5 and n=-1 would be valid return values, right?
Can I be entirely certain that Lua always returns 0 for the example table, or any other table containing only negative indices? If (hypothetically) I'd be writing a Lua interpreter and would return either of those values, would I be conforming with the specifications?
Edit
Obviously, the way Lua is implemented, it does not return negative values. I felt the length operator is somewhat underdocumented and I see that Lua 5.2's documentation has changed. It now says:
Unless a __len metamethod is given, the length of a table t is only defined if the table is a sequence, that is, the set of its positive numeric keys is equal to {1..n} for some integer n. In that case, n is its length. Note that a table like
{10, 20, nil, 40}
is not a sequence, because it has the key 4 but does not have the key 3.
So, it now talks about positive numeric keys, that's much clearer. I'm left wiser but not totally happy with the documentation. When it says the "length is only defined if the table is a sequence", it should also state that even if the table is not a sequence a value is returned, but the behavior is undefined. Also, this table looks pretty much like a sequence:
a = setmetatable(
{0},
{
__index = function(t,k)
return k < 10 and k or nil
end
}
)
i = 1
while a[i] do
print(a[i])
i = i+1
end
--[[ prints:
0
2
3
4
5
6
7
8
9
]]
print(#a)
-- prints: 1
However, this is becoming nitpicking as it's pretty clear that it wouldn't make sense to take into account what mess __index might make. And Stackoverflow is certainly not the place to complain about documentation that could be more precise.
As you have noted, the specification of the length operator has changed between 5.1 and 5.2.
Can I be entirely certain that Lua always returns 0 for the example table, or any other table containing only negative indices?
You can for the current reference implementation, which ensures that for ilen defined
function ilen (xs)
local i=0
while xs[i+1] do i=i+1 end
return i
end
we always have #xs >= ilen(xs) - see the definition of luaH_getn in the ltable.c source. But the specification now deliberately does not promise this behaviour: a conformant implementation can return nil or raise an exception for attempts to find the length of tables that are not sequences.
From the text in reference link. The answer is NO.
I think your confusing the fact that if a NIL is found then the length of the table is deemed to be position the NIL was found -1.
Therefore if t(1) is NIL then 1 - 1 = 0 so the table length is 0.
If the length of a table was 5 then the next position or t(6) IS or WOULD BE NIL
The length of a table t is defined to be any integer index n such that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil, n can be zero.

Resources