Using tables in other files - lua

Edit I got this working, I'm not sure if this is the right way to do it, but this is what works right now
I just started learning Lua, and I'm trying to figure out how to pass tables between files so that I can have a more organized codespace. I have read through the book Programming in Lua, and for some reason, I can't figure out what i'm doing wrong.
The problem i'm getting is this error:
lua: Test2.lua:3: attempt to call method 'New' (a nil value)
From this code:
--Test.lua----------------
module("Test", package.seeall)
vector = require "./Hump/vector"
Bot = {}
Bot.position = vector.new(0,0)
function Bot:New(object)
object = object or {}
setmetatable(object, self)
self.__index = self
return object
end
--Test2.lua------------------
require "Test"
Bot1 = Test.Bot:New()
print(Bot1.position)
As far as I understand it, this error means that it cannot find the method new, it is effectively undefined. I thought that require imports the file in the path?

Bot is an empty table.
local B = {} -- initialize local B with new table
Bot = B -- Bot now references the same table as B
B = { position = vector.new(0,0) } -- here you create a NEW table, B ~= Bot now
function B:New(object) -- store New function in B table, Bot still empty
So you're returning an empty table.
No need for two variables here at all.
local Bot = {
-- stuff
}
function Bot:New(object)
-- stuff
end
return Bot

Related

how is a function navigating through fields of a table

i already asked this question on stackoverflow and accepted an answer i think i did not really understand the answer and i got some more question, im embarrassed to necro it so im creating a new question
im learning lua and got to metatable part, in this example
local tb = {}
local meta = {}
function tb.new(s)
local super = {}
super.s = s
setmetatable(super,meta)
return super
end
function tb.add(s1,s2)
return s1.s..s2.s
end
meta.__add = tb.add
f= tb.new("W")
t= tb.new("E")
print(f+t)
when compiler gets tof = tb.new("W")
i think this happens
function tb.new("W") super.W = W return setmetatable(super,meta) return super end
so
print(f+t)
looks like
print(super+super)
how does
tb.add(super,super)
find the fields of super table using
return s1.s..s2.s
also as the
tb.new
function is called twice and
setmetatable(super,meta)
happens twice is there any difference between the first and second iteration? if any of the above are incorrect please correct me.
when compiler gets tof = tb.new("W") i think this happens function tb.new("W") super.W = W return setmetatable(super,meta) return super end
No. It's more like super['s'] = 'W'. That's how dot notation works. I hope that clarifies how Lua "finds the fields" later.
as the tb.new function is called twice and setmetatable(super,meta) happens twice is there any difference between the first and second iteration?
They're different, because the super variable is a new table each time. Any time you see {} (a table constructor), whether empty or not, it's creating an entirely new table. meta, however, is still the same table, because it only gets a table constructor once, outside of any function.

LUA - Create Meta Table with every sub table beeing a meta table again

but this will get confusing for sure.
I'm still very new to LUA and one thing i haven't worked much yet with is metatables.
I need to find a way to create a meta table which runs a function on editing values. This is not a problem if i stay on "one level", so the first index. Then i can simply use the __newindex to run it. But what i'm trying to do is to run the function whenever any value is changed.
This would require some way to set any table inside the metatable to be again a metatable running the same function as the "main" metatable
In my use case that would be a "save" function:
function MySaveFunction(tbl)
FileSave(my_settings_path, tbl)
end
MyTable = setmetatable()
MyTable.Value = value --> run MySaveFunction(MyTable.Value)
MyTable.SubTable = {} --> run setmetatable() on SubTable
MyTable.SubTable.Value = value --> run MySaveFunction(MyTable.SubTable.Value)
MyTable.SubTable.SubSubTable = {} --> run setmetatable() on SubSubTable
MyTable.SubTable.SubSubTable.Value = value --> run MySaveFunction(MyTable.SubTable.SubSubTable.Value)
MyTable.SubTable.SubSubSubTable = {} --> run setmetatable() on SubSubSubTable
MyTable.SubTable.SubSubSubTable.Value = value --> run MySaveFunction(MyTable.SubTable.SubSubSubTable.Value)
Hope someone can help me <.<
First thing to take a note of is that __newindex and __index metamethods are only triggered when they handle nil value in target table. If you want to track every single change, you can't just use __newindex, because once you write a value, subsequent calls won't do anything. Instead, you need to use a proxy table.
Another important thing to consider is tracking paths of accessed members.
Last, but not least, you need to remember to use raw access functions like e.g. rawget when implementing your handlers. Otherwise, you may encounter stack overflows or other weird behaviour.
Let's have a trivial example to illustrate the problems:
local mt = {}
function mt.__newindex (t, key, value)
if type(value) == "table" then
rawset(t, key, setmetatable(value, mt)) -- Set the metatable for nested table
-- Using `t[key] = setmetatable(value, mt)` here would cause an overflow.
else
print(t, key, "=", value) -- We expect to see output in stdout for each write
rawset(t, key, value)
end
end
local root = setmetatable({}, mt)
root.first = 1 -- table: 0xa40c30 first = 1
root.second = 2 -- table: 0xa40c30 second = 2
root.nested_table = {} -- /nothing/
root.nested_table.another = 4 -- table: 0xa403a0 another = 4
root.first = 5 -- /nothing/
Now, we need to deal with them. Let's start with a way to create a proxy table:
local
function make_proxy (data)
local proxy = {}
local metatable = {
__index = function (_, key) return rawget(data, key) end,
__newindex = function (_, key, value)
if type(value) == "table" then
rawset(data, key, make_proxy(value))
else
print(data, key, "=", value) -- Or your save function here!
rawset(data, key, value)
end
end
}
return setmetatable(proxy, metatable) -- setmetatable() simply returns `proxy`
end
This way you have three tables: proxy, metatable and data. User accesses proxy, but because it's completely empty on each access either __index or __newindex metamethods from metatable are called. Those handlers access data table to retrieve or set the actual values that user is interested in.
Run this in the same way as previously and you will get an improvement:
local root = make_proxy{}
root.first = 1 -- table: 0xa40c30 first = 1
root.second = 2 -- table: 0xa40c30 second = 2
root.nested_table = {} -- /nothing/
root.nested_table.another = 4 -- table: 0xa403a0 another = 4
root.first = 5 -- table: 0xa40c30 first = 5
This should give you an overview on why you should use a proxy table here and how to handle metamethods for it.
What's left is how to identify the path of the field that you are accessing. That part is covered in another answer to another question. I don't see a reason to duplicate it.

Filling Lua table not going as expected

I am coding a little something in Lua and I've encountered a very frustrating bug/mistake in my code.
network = {}
network.neurons = {}
for i=1,4 do
network.neurons[20000] = {}
network.neurons[20000][i] = NewNeuron()
print(network.neurons[20000][i])
end
The NewNeuron() function creates a new object with some variables. The print() inside this for loop returns the table with the correct variables as expected. The problem comes when I try to use this print again in this loop:
for i=1,4 do
print(network.neurons[20000][i])
end
The print then writes 4 console lines as follows:
(no return)
(no return)
(no return)
*neuron info that should be printed*
It looks as if only the last of the 4 objects exists after I exit the creation loop. Why is this? What am I doing wrong?
You are assigning an entirely new table inside the loop when creating a NewNeuron. The declaration should be outside:
network = {}
network.neurons = {}
network.neurons[20000] = {}
for i=1,4 do
network.neurons[20000][i] = NewNeuron()
print(network.neurons[20000][i])
end

Lua table as frontend to database

I am attempting to implement a database as a Lua table. Using metatables, this table would be empty, and when an item is requested or modified in the table, it would return or modify the item in the database. The database itself would never be loaded into memory, except for the parts which are requested. It should be interacted with by the program as a table (as it is a table). The table, since it's only a "front", would save modified data to the database (rather than defining that item in the table).
In a table with no tables inside of it, this is easy to implement. I'm trying to make it work with a multi-layered table of indefinite depth.
(Aside: The database I'm considering is redis. Ideally this could be implemented for any database or database-like server by only changing the basic operating syntax.)
Because of the behavior of Lua's metatables, the __newindex method is only used when something is modified in the top-level (or created, if you're using a proxy). The __index method is called when something is read, even if the call is to modify something within a sub-table. Because of this, I'm trying to write an __index method that, when a table is requested, returns yet another pseudo-proxy (a table proxying a database rather than another tables) with the same behavior, except the proxy is for the table/array/list within the top-level, etc, to an indefinite depth. I am struggling.
My questions are:
Has this been implemented before?
Should I be focusing on "proper"
class system rather than what I'm doing now?
When you create a table, simply add an empty table in the fake and set it's metatable:
local fake = {}
do
local lookup = {} --Will be using this to avoid using lots of metatables
local real = {}
local meta
meta = {
__index = function(self,i)
return rawget(lookup[self], i)
end,
__newindex = function(self,i,v)
rawset(lookup[self], i, v)
if type(v) == "table" then
rawset(self, i, setmetatable({},meta))
lookup[self[i]] = v
end
end
}
setmetatable(fake, meta)
lookup[fake] = real
end
fake[1] = "hello"
print(fake[1])
print(rawget(fake, 1))
fake.x = {"hi"}
print(fake.x)
print(rawget(fake, 'x')) --This still prints a table because there actually is one, but in reality it's abiding by our rules
print(fake.x[1])
print(rawget(fake.x, 1))
fake.x.y = "aha"
print(fake.x.y)
print(rawget(fake.x, 'y'))
The only caveat with this method is they can directly modify the database like so:
fake.myvalue = {}
fake.myvalue = 5
Another method could be to wrap as you go:
local fake = {}
do
local lookup = {} --Will be using this to avoid using lots of metatables
local cache = {} --will be using to avoid usings tons of new objects
local real = {}
local meta
meta = {
__index = function(self,i)
local val = rawget(lookup[self], i)
if type(val) == "table" then
if cache[val] then
return cache[val]
else
local faker = {}
lookup[faker] = val
cache[val] = faker
return setmetatable(faker, meta)
end
else
return val
end
end,
__newindex = function(self,i,v)
rawset(lookup[self], i, v)
end
}
setmetatable(fake, meta)
lookup[fake] = real
end
fake[1] = "hello"
print(fake[1])
print(rawget(fake, 1))
fake.x = {"hi"}
print(fake.x)
print(rawget(fake, 'x')) --This still prints a table because there actually is one, but in reality it's abiding by our rules
print(fake.x[1])
print(rawget(fake.x, 1))
fake.x.y = "aha"
print(fake.x.y)
print(rawget(fake.x, 'y'))
Which avoids the direct modifying problem completely

I need clarification on Metatable.__index

I asked earlier why my methods for a metatable weren't being located by Lua, and was told that by setting __index to my metatable, that it would resolve the issue, so I assumed that a method when called was searching by index in the metatable, but I've ran into an issue now that I need to use indexing brackets [ and ] on my metatable, so __indexis assigned to return an index from a table inside of it, how do I resolve the functionality needs of both using methods, and use of indexing brackets
I wrote a minimal example indicating the problem:
TestMetatable = {DataTable = {}}
TestMetatable.__index = TestMetatable
function TestMetatable.new()
local Tmp = {}
setmetatable(Tmp,TestMetatable)
Tmp.DataTable = {1}
return Tmp
end
function TestMetatable:TestMethod()
print("Ran Successfully")
end
function TestMetatable.__index(self,index)
return self.DataTable[index]
end
local Test = TestMetatable.new()
-- both functionalities are needed
print(Test[1])
Test:TestMethod()
You need to understand the difference between __index and __newindex, and their relationship with the current contents of the main table.
__newindex is only called/accessed when all the following are true:
When you are setting a value into the main table, via tbl[index] = expr (or equivalent syntax, like tbl.name = expr).
When the key you are trying to set into the main table does not already exist in the main table.
The second one trips people up often. And that's your problem here, because __index is only accessed when:
When the key being read from the main table does not already exist in the main table.
So if you want to filter every read from and write to a table, then that table must always be empty. Therefore, those reads and writes need to go into some other table you create for each new object. So your new function needs to create two tables: one that remains empty and one that has all the data in it.
Honestly, I wish Lua had a way to create just an empty piece of userdata that you could bind a user-defined metatable to, just to avoid these issues.
the way I resolved this problem, according to Nicol Bolas's solution, if it might give clarity to anyone else's confusion :-)
TestMetatable = {DataTable = {}, FunctionTable = {}}
function TestMetatable.new()
local Tmp = {}
setmetatable(Tmp,TestMetatable)
Tmp.DataTable = {1}
Tmp.FunctionTable = TestMetatable
return Tmp
end
function TestMetatable:TestMethod()
print("Ran Successfully")
end
function TestMetatable.__index(self,index)
if type(index) == "string" then
return self.FunctionTable[index]
else
return self.DataTable[index]
end
end
local Test = TestMetatable.new()
-- both functionalities are needed
print(Test[1])
Test:TestMethod()

Resources