Lua Memory Control About Table - memory

if I have structure like this:
tbl1 = {}
tbl2 = {}
tbl1.t1 = tbl2
-- release
tbl1 = nil
Then what is gonna happen to tbl2? Will it be released as well?

Yes. Lua uses a mark and sweep algorithm for garbage collection, so unreachable yet circularly referenced data structures will be collected.
(In your code, it won't be released, as tbl2 still contains a reference to the table, but I'm assuming you intended to clear that one too.)

Related

Performance of accessing table via reference vs ipairs loop

I'm modding a game. I'd like to optimize my code if possible for a frequently called function. The function will look into a dictionary table (consisting of estimated 10-100 entries). I'm considering 2 patterns a) direct reference and b) lookup with ipairs:
PATTERN A
tableA = { ["moduleName.propertyName"] = { some stuff } } -- the key is a string with dot inside, hence the quotation marks
result = tableA["moduleName.propertyName"]
PATTERN B
function lookup(type)
local result
for i, obj in ipairs(tableB) do
if obj.type == "moduleName.propertyName" then
result = obj
break
end
end
return result
end
***
tableB = {
[1] = {
type = "moduleName.propertyName",
... some stuff ...
}
}
result = lookup("moduleName.propertyName")
Which pattern should be faster on average? I'd expect the 'native' referencing to be faster (it is certainly much neater), but maybe this is a silly assumption? I'm able to sort (to some extent) tableB in a order of frequency of the lookups whereas (as I understand it) tableA will have in Lua random internal order by default even if I declare the keys in proper order.
A lookup table will always be faster than searching a table every time.
For 100 elements that's one indexing operation compared to up to 100 loop cycles, iterator calls, conditional statements...
It is questionable though if you would experience a difference in your application with so little elements.
So if you build that data structure for this purpose only, go with a look-up table right away.
If you already have this data structure for other purposes and you just want to look something up once, traverse the table with a loop.
If you have this structure already and you need to look values up more than once, build a look up table for that purpose.

How are tables managed in Lua? [Example of extrange behaviour]

I was trying to understand a Lua function to parse XML-formatted strings and I observed an unexpected behaviour on how tables were being handled. I summarised the problem as follows:
local stack = {}
local top = {}
table.insert(stack, top)
table.insert(top,{1,2,3})
top={'x','y','z'}
print(stack[1][1][1],stack[1][1][2],stack[1][1][3])
print(top[1],top[2],top[3])
>> 1 2 3
>> x y z
I don't get why the two outputs are not the same. If top is inserted into stack by reference, the first output would make sense if I didn't overwrite top with top={'x','y','z'}. Why isn't stack affected when I directly enter the values of top?
It looks as if top={'x','y','z'} created another sort of instance for top, in such a way that the values pointed by stack are being kept. Is this right? Lua documentation is scarce when you want to go into detail and I haven't found anything about this. It seems to me that this is a dangerous behaviour if it is not explicitly specified.
Regards
_______Edit:_______
I was making a mistake: table.insert(top,{1,2,3}) is not the same as top={'x','y','z'}, it is the same as top[1]={'x','y','z'}.
So, reformulating the original question, we have the code:
local stack = {}
local top = {}
table.insert(stack, top)
table.insert(top,{1,2,3})
top[1]={4,5,6} -- it does change stack
top = {'x'}
print(stack[1][1][1],stack[1][1][2],stack[1][1][3])
print(top[1][1],top[1][2],top[1][3])
>> 4 5 6
>> nil nil nil
Now, I see perfectly normal the second output, but I still have doubts predicting the first one.
If I replace top = {'x'} for top[1] = 'x' the output turns to
>> nil nil nil
>> nil nil nil
I have read the Values and Types section, but I still don't fully know what's wrong.
Sorry if I am making a silly mistake, but I can't see it.
Lua operates on values, not on variables.
Try replacing
top={'x','y','z'}
by
top[2]='y'
Tables are values in Lua. Each table is a separate and distinct value. Variables are not names for values; they're just boxes that hold them for a time.
Multiple variables can refer to the same table. So after doing top = {}, the variable top has a reference to a table. By doing table.insert(stack, top), now both top and stack[1] have a reference to the same table.
If you execute top = {'x'}, then you change what table is stored in top. You have put a new thing in that box. But stack[1] still refers to the table that was set into it.
It's not different from doing this:
var1 = 5;
var2 = var1;
var1 = 10;
var2 is still five; it didn't become 10. var2 has no connection to var1. It simply took the value that var1 had at that time.
Where you're getting confused is in the difference between the table itself and its contents.
top contains a reference to a table. top[1] means to get the first element of the table referenced by top. If you assign something to top[1], you are not modifying the variable; you are modifying the contents of the table referenced by that variable.
top[1] = {4, 5, 6} modifies what is stored in the table currently referenced by top. And at that time, that table is also being referenced by stack[1]. Therefore, you can access that table through top or through stack.
Once you change what table top references, by doing top = {'x'}, that is no longer true. top now references a different table. But the table it used to reference is still accessible via stack.

Removing Metatables from a table in Lua

I want to "unhook" a metatable from a table and was wondering if:
tbl = setmetatable(tbl, false) -- or nil
is the correct way to do this? I could not find any info about how to do it correctly. Do I need to use an assignment operator?
Also, would this be enough to destroy the metatable attached to the table if the metatable never had a reference and was anonymous?:
tbl = setmetatable({}, {__index = something})
-- later on:
tbl = nil
and the garbage collector would be enough to remove both tables?
According to the Lua reference, which you should always consult befor putting up a question here, setmetatable(tbl, nil) will delete the metatable of table tbl unless tbl's original metatable is protected. Or let's better say it does not delete the metatable but the reference to it. The table that served as metatable will of course not be deleted as long as there are other references to it.
Befor you ask people if a simple function call works, try it yourself.
You can use https://www.lua.org/cgi-bin/demo or any other Lua interpreter and you get your answer in seconds without involving anyone else.
Running this code:
setmetatable({}, false)
or
setmetatable({})
will result in
input:1: bad argument #2 to 'setmetatable' (nil or table expected)
Now you know that you cannot enter false and you have to enter nil explicitly.
To checkout that __metatable thing you would have read in the reference manual you could then try this code
local tbl = setmetatable({}, {__metatable = true})
setmetatable(tbl, nil)
Which results in the following output:
input:2: cannot change a protected metatable
To the second part of your question:
tbl = nil will not delte the table referenced by tbl. It will only remove the reference tbl to it.
local a = {}
local b = a
b = nil
print(a)
a is still a table. You only removed one of its references.
Once there is no reference left the garbage collector may collect the table.
setmetatable(tbl, {}) will establish a reference to the table returned by the table constructor {} and store that reference somewhere in the guts of tbl.
If tbl was the last reference to that table it will be collected as garbage at some point. Then of course the only reference to the table you set as metatable will also be gone and it will removed as well.
If you do something like that:
local a = {}
local b = setmetatable({}, a)
,
a = nil will not delete b's metatable
So yes it will remove both tables if no other reference to either one of them is left.

Lua 5.0 - iterations over tables ignore duplicate keys even though values are different

Reading the injected comments in the Code Snippet should give enough context.
--| Table |--
QuestData = {
["QuestName"]={
["Quest Descrip"]={8,1686192712},
["Quest Descrip"]={32,1686193248},
["Quest Descrip"]={0,2965579272},
},
}
--| Code Snippet |--
--| gets QuestName then does below |--
if QuestName then
-- (K = QuestName) and (V = the 3 entries below it in the table)
for k,v in pairs(QuestData) do
-- Checks to make sure the external function that obtained the QuestName matches what is in the table before cont
if strlower(k) == strlower(QuestName) then
local index = 0
-- Iterates over the first two pairs - Quest Descrip key and values
for kk,vv in pairs(v) do
index = index + 1
end
-- Iterates over the second two pairs of values
if index == 1 then
for kk,vv in pairs(v) do
-- Sends the 10 digit hash number to the function
Quest:Function(vv[2])
end
end
end
end
end
The issue I'm running into is that Lua will only pick up one of the numbers and ignore the rest. I need all the possible hash numbers regardless of duplicates. The QuestData table ("database") has well over 10,000 entries. I'm not going to go through all of them and remove the duplicates. Besides, the duplicates are there because the same quest can be picked up in more than one location in the game. It's not a duplicate quest but it has a different hash number.
Key is always unique. It is the point of the key, that the key is pointing to unique value and you can't have more keys with same name to point different values. It is by definition by Lua tables.
It is like if you would want to have two variables with same name and different content. It does not make sense ...
The table type implements associative arrays. [...]
Like global variables, table fields evaluate to nil if they are not initialized. Also like global variables, you can assign nil to a table field to delete it. That is not a coincidence: Lua stores global variables in ordinary tables.
Quote from Lua Tables
Hashing in Lua
Based on comments, I update the answer to give some idea about hashing.
You are using hashing usually in low-level languages like C. In Lua, the associative arrays are already hashed somehow in the background, so it will be overkill (especially using SHA or so).
Instead of linked lists commonly used in C, you should just construct more levels of tables to handle collisions (there is nothing "better" in Lua).
And if you want to have it fancy set up some metatables to make it somehow transparent. But from your question, it is really not clear how your data look like and what you really want.
Basically you don't need more than this:
QuestData = {
["QuestName"]={
["Quest Descrip"]={
{8,1686192712},
{32,1686193248},
{0,2965579272},
},
},
}
As Jakuje already mentioned table keys are unique.
But you can store both as a table member like:
QuestData = {
-- "QuestName" must be unique! Of course you can put it into a table member as well
["QuestName"]={
{hash = "Quest Descrip", values = {8,1686192712} },
{hash = "Quest Descrip", values = {32,1686193248} },
{hash = "Quest Descrip", values = {0,2965579272} }
}
}
I'm sure you can organize this in a better way. It looks like a rather confusing concept to me.
You've said you can't "rewrite the database", but the problem is the QuestData table doesn't hold what you think it holds.
Here's your table:
QuestData = {
["QuestName"]={
["Quest Descrip"]={8,1686192712},
["Quest Descrip"]={32,1686193248},
["Quest Descrip"]={0,2965579272},
},
}
But, this is actually like writing...
QuestData["Quest Descrip"] = {8,1686192712}
QuestData["Quest Descrip"] = {32,1686193248}
QuestData["Quest Descrip"] = {0,2965579272}
So the second (and then, third) values overwrite the first. The problem is not that you can't access the table, but that the table doesn't contain the values any more.
You need to find a different way of representing your data.

table.remove in lua acting weirdly

tbl1 = {1}
tbl2 = tbl1
table.remove(tbl2,1)
print(tbl1[1])
-- >> nill
The above example is a simplification of the problem in my code, by removing a index from tbl2, it also removes from tbl1, is there a reason for this to be happening?
Variables in Lua are references to objects, and so a=b sets the variable named a to refer to the object that b refers to. If b is a table, then after the assignment both a and b point to the same table object.

Resources