I'm trying to create a Lua table that represents a matrix, however I keep running into a problem where if I create two Matrices, and initialize some values they both have the same values.
--Test.Lua
require"Matrix"
M1 = Matrix.Matrix:New()
M2 = Matrix.Matrix:New()
M1._11 = 2
print(M1._11) --Prints 2
print(M2._11) --Prints 2
--Matrix.lua
module("Matrix", package.seeall)
Matrix = {}
Matrix = { _11 = 0, _12 = 0, _13 = 0,
_21 = 0, _22 = 0, _23 = 0,
_31 = 0, _32 = 0, _33 = 0
}
function Matrix:New()
object = object or {}
setmetatable(object, self)
self.__index = self
return object
end
object = object or {}
This is why that happens. You only ever create one Matrix object. There is only every one object table which you return, and there is only ever one self table that you use as a metatable.
So how can you expect different instances when Matrix:New will always return the exact same value on every call?
You need to return a new table for each New call; that's why we use that name ;) Because of the way you're using a metatable, you also have to return a new metatable; you can't return the same metatable attached to new tables and expect it to work.
As nicol is explaining, on one hand you are trying to "reuse the same object over and over" (probably to "make it faster") and on the other you want to have different objects.
The solution is - don't reuse object on New call.
local Matrix = {} -- don't use the module function. Make Matrix local ...
Matrix.__index = Matrix
function Matrix:New()
local object = { -- create one local variable on every call to New
_11 = 0, _12 = 0, _13 = 0,
_21 = 0, _22 = 0, _23 = 0,
_31 = 0, _32 = 0, _33 = 0
}
setmetatable(object, self)
return object
end
return Matrix -- ... and return the Matrix local var at the end
A couple notes:
You really must learn how to use local
Usage of the module function is not recommended. Return a local table instead, as in my example.
Usage: assuming that that file is called "Matrix.lua":
local Matrix = require 'Matrix'
local M1 = Matrix:New()
local M2 = Matrix:New()
-- etc
As a sidenote, the Matrix:New() function can be made shorter (and faster). The following implementation works exactly as the one above, but it's slightly more efficient:
function Matrix:New()
return setmetatable({
_11 = 0, _12 = 0, _13 = 0,
_21 = 0, _22 = 0, _23 = 0,
_31 = 0, _32 = 0, _33 = 0
},
self)
end
This works because setmetatable(t,m) returns t with m already set as its metatable.
Related
How do I get the full path to the required value in the table? I want to track changes in another table through a proxy table.
I understand that I need to use metatables and __index in it. But I haven't been able to come up with a tracker yet.
Sample table structure:
Objects = {
Panel = { layer = 1, x = 600, y = 328, w = 331, h = 491;
objects = {
label = { layer = 1, x = 0, y = 0, text = 'header' };
Window = { layer = 2, x = 400, y = 100, w = 100, h = 100;
objects = {
label = { layer = 1, x = 0, y = 0, text = 'lorem ipsum dorem' };
};
};
};
};
};
Path: Objects.Panel.objects.Window.objects.label.text
I tried to create a metatable for each of the tables and collect the result of each call to __index into a table in order to roughly understand which key and value were retrieved or changed in order to synchronize these values with other tables.
This will prove itself to be horrendously slow and memory inefficient. Anyway, you were right on the track: proxy and handle __index and __newindex metamethods to your liking. This being said you also need to track the state of the proxy somehow.
You can try to hide it with some closures and upvalues but the easy way is to store the information directly in the proxy tables:
function make_tracker (o, name)
local mt = {}
mt.__index = function (proxy, key)
local path = {unpack(rawget(proxy, "__path"))} -- Stupid shallow copy
local object = rawget(proxy, "__to")
table.insert(path, key)
if type(object[key]) == "table" then
return setmetatable({__to = object[key], __path = path}, mt)
else
return table.concat(path, ".") .. " = " .. tostring(object[key])
end
end
return setmetatable({__to = o, __path = {name}}, mt)
end
__to fields indicates what proxy should point to and __path is there to cover fields we have trespassed so far. It does a shallow copy, so that one can use subproxies with local variables. name parameter is there to initialize the name of the first table, as you just simply can't know that. You use it like this:
local tObjects = make_tracker(Objects, "Objects")
local subproxy = tObjects.Panel.objects.Window
print(subproxy.objects.label.text)
print(tObjects.Panel.objects.label.text)
print(subproxy.x)
-- prints:
-- Objects.Panel.objects.Window.objects.label.text = lorem ipsum dorem
-- Objects.Panel.objects.label.text = header
-- Objects.Panel.objects.Window.x = 400
Of course, I doubt that appending the path to the original value is what you want. Modify insides of else block:
return table.concat(path, ".") .. " = " .. tostring(object[key])
according to your needs, e.g:
register_tracked_path(table.concat(path, "."))
return object[key]
If you want to handle modification of values you need to extend the metatable with similar __newindex.
I have a nested table of global variables in my lua file like
GLOBAL.ADVANTAGES =
{
CLASS =
{
WARRIOR =
{
ATTACK = 0,
DEFEND = 0,
CRIT_CHANCE = 0,
CRIT_DAMAGE = 0
},
PALADIN =
{
ATTACK = 0,
DEFEND = 0,
CRIT_CHANCE = 0,
CRIT_DAMAGE = 0
},
PRIEST =
{
ATTACK = 0,
DEFEND = 0,
CRIT_CHANCE = 0,
CRIT_DAMAGE = 0
},
...
...
I want to traverse table to return all variable names in strings like "GLOBAL.CLASS.PRIEST.ATTACK", "GLOBAL.CLASS.PRIEST.DEFENSE", and so on. I already can traverse whole table but It returns values solely as "ATTACK", "DEFENSE" and such...
How can I get full "address" of each value?
More efficient and easier way is to use table.concat. For example, to show all fields:
local myprint
myprint = function(tab, prefixtab) -- Tab = tab to print, prefix = inital tab ({'GLOBAL'} in your case) or nil
prefixtab = prefixtab or {}
local idx = #prefixtab+1
for k,v in pairs(tab) do
prefixtab[idx] = k
print(table.concat(prefixtab, '.')) -- Comment to don't print for every path
if type(v) == 'table' then
myprint(v, prefixtab)
--else -- To print only complete pathes
--print(table.concat(prefixtab, '.'))
end
end
prefixtab[idx] = nil
end
Example (based on your table):
> myprint(GLOBAL, {'GLOBAL'})
GLOBAL.ADVANTAGES
GLOBAL.ADVANTAGES.CLASS
GLOBAL.ADVANTAGES.CLASS.WARRIOR
GLOBAL.ADVANTAGES.CLASS.WARRIOR.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.WARRIOR.ATTACK
GLOBAL.ADVANTAGES.CLASS.WARRIOR.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.WARRIOR.DEFEND
GLOBAL.ADVANTAGES.CLASS.PRIEST
GLOBAL.ADVANTAGES.CLASS.PRIEST.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.PRIEST.ATTACK
GLOBAL.ADVANTAGES.CLASS.PRIEST.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.PRIEST.DEFEND
GLOBAL.ADVANTAGES.CLASS.PALADIN
GLOBAL.ADVANTAGES.CLASS.PALADIN.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.PALADIN.ATTACK
GLOBAL.ADVANTAGES.CLASS.PALADIN.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.PALADIN.DEFEND
Or complete-only path version:
> myprint(GLOBAL, {'GLOBAL'})
GLOBAL.ADVANTAGES.CLASS.PRIEST.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.PRIEST.ATTACK
GLOBAL.ADVANTAGES.CLASS.PRIEST.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.PRIEST.DEFEND
GLOBAL.ADVANTAGES.CLASS.WARRIOR.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.WARRIOR.ATTACK
GLOBAL.ADVANTAGES.CLASS.WARRIOR.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.WARRIOR.DEFEND
GLOBAL.ADVANTAGES.CLASS.PALADIN.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.PALADIN.ATTACK
GLOBAL.ADVANTAGES.CLASS.PALADIN.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.PALADIN.DEFEND
Assuming you are doing a depth-first traversal (aka in-order), as you descend the hierarchy, pass down the path for the parent node. At the point where you capture the leaf's name, prepend it with the parent node's path. If the node is not a leaf, prepend the node's name with the parent node's path and pass it down.
local node = path .. "." .. tostring(key) -- tostring used in case key is not a string
Use "GLOBAL" as the initial path.
I'm working on a game where a bunch of characters will be generated on the fly, based on some constraints defined either in the project or externally via mod files. I am using MoonSharp Lua (5.2) interpreter for interfacing with my C# code, and Lua tables to store the constraint presets. As an example:
require "Defaults"
AgePresets = {}
-- Single value
AgePresets.Newborn = 0
-- Simple ranges
AgePresets.Default = defaultAgeRange --referring to the Defaults require
AgePresets.Child = {1, 12}
AgePresets.Teenager = {13, 19}
AgePresets.YoungAdult = {20, 29}
AgePresets.Adult = {30, 40}
AgePresets.MiddleAge = {40, 60}
AgePresets.Senior = {61, 80}
AgePresets.Elder = {81, 99}
AgePresets.Methuselah = {100, 150}
AgePresets.Methuselah2 = {150, 200}
-- Weighted ranges // again referring to previously defined elements to keep things concise
AgePresets.Tween = {
{weight = 1, minmax = AgePresets.Teenager },
{weight = 1, minmax = AgePresets.YoungAdult }
}
This works fine, but from an end-user point of view, there's a lot of unnecessary typing involved. We are clearly working on AgePresets here but it is still mentioned as a prefix before every member name.
I could of course define AgePresets as an array, like AgePresets = { Child = {}, Teenager = {} } but the problem with that is then I cannot refer to previously defined elements in the array.
This doesn't work:
AgePresets = {
Child = {1,12},
RefToChild = Child, //attempt to index a nil value exception
Teen = {13,19}
}
What I ideally want to achieve is a clean, concise way for users to enter this data in, like in the first example but without having to put AgePresets. prefix before everything. How do I go about declaring a scope in a file such that all succeeding members defined in the file will be within that scope, while maintaining the ability to refer to other members defined previously in the scope?
AgePresets = setmetatable({}, {__index = _G})
do
local _ENV = AgePresets
Newborn = 0
Child = {1,12}
RefToChild = Child -- this ref is Ok
Teen = {13,19}
YoungAdult = {20,29}
Tween = {
{weight = 1, minmax = Teen },
{weight = 1, minmax = YoungAdult }
}
rnd = math.random(10) -- global functions are available here
end
setmetatable(AgePresets, nil)
You can mix the two styles: table constructor for fields that don't need to reference variables that aren't in scope yet, followed by assignment statements for the rest.
I would do that unless the order of the fields in the code significantly enhanced comprehension.
I'm trying to index a parent object by using its identifier, but it returns nil instead of the object, so it throws a error when executing the script.
local mapit = {
...
ground = function(x, y, w, h, data)
...
local id = 0
-- mapit is nil in this block
for i = 0, #mapit.data.ids do
if id ~= i then
id = id + 1
end
end
...
end,
data = {
ids = {}
}
...
}
local myRect = mapit.ground(400, 100, 600, 100)
In Lua, locals are not in scope on the right hand of their initializer, so your closure refers to a global named mapit instead.
Declare the local first, then assign to it.
local mapit
mapit = { ... }
I am trying to construct a method that automatically adds and renames objects in Lua. I have the add method for adding the object, but I am not sure how to make it so that it renames each object. I'm thinking of adding an if statement, but I don't know how to construct it in a way that it will rename the object each type it loops.
Here is what I have so far:
frogBody = {density = .8, friction = 0.3, bounce = 0.1, radius = 10} -- body Type
local onPlayerSpawnObject = function(object) -- method to spawn object
local layer = map:getTileLayer("Enemies")
local frog = movieclip.newAnim{ "FrogMini.png", "frogMiniRed.png" } -- object that spawns
frog.x = object.x ; frog.y = object.y
frog.myName = "frog"
frog.isHit = false
physics.addBody(frog, frogBody)
end
Thank you for all of your help!
I think that what you mean is that you would like to access the frogs objects from your example distinctively ?
If that is the case you could either assign the return of the add function to a lua table, or directly assign to the table inside your function.
local frogs = {}
frogBody = {density = .8, friction = 0.3, bounce = 0.1, radius = 10} -- body Type
local onPlayerSpawnObject = function(object) -- method to spawn object
local layer = map:getTileLayer("Enemies")
local frog = movieclip.newAnim{ "FrogMini.png", "frogMiniRed.png" } -- object that spawns
frog.x = object.x ; frog.y = object.y
frog.myName = "frog"
frog.isHit = false
physics.addBody(frog, frogBody)
table.insert(frogs, frog)
end
-- To print a distinct frog from the collection, replace 1 by your index
print (frogs[1].myName)