I am confused of the following two syntaxes using "."
From what I understand, __index is called when a key doesn't exist in a table but exists in its metatable. So why does the list table call __index and then assign itself to list.__index?
list = {}
list.__index = list
setmetatable(list, { __call = function(_, ...)
local t = setmetatable({length = 0}, list)
for _, v in ipairs{...} do t:push(v) end
return t
end })
function list:push(t)
if self.last then
self.last._next = t
t._prev = self.last
self.last = t
else
self.first = t
self.last = t
end
self.length = self.length + 1
end
.
.
.
local l = list({ 2 }, {3}, {4}, { 5 })
Does Window.mt simply create a table? Why do we need Window = {} as a namespace here?
Window = {} -- create a namespace
Window.mt = {} -- create a metatable
Window.prototype = {x=0, y=0, width=100, height=100, }
function Window.new (o)
setmetatable(o, Window.mt)
return o
end
Window.mt.__index = function (table, key)
return Window.prototype[key]
end
w = Window.new{x=10, y=20}
print(w.width) --> 100
So why does the list table call __index and then assign itself to list.__index?
Nowhere in your code does the list table call __index. The assignment part however is a common Lua idiom (aka. hack) to save some memory. Conceptually there are 4 different kinds of tables involved:
list objects (the tables created via {length=0} in your code)
a metatable (containing an __index field) that modifies the behavior of list objects when you try to access non-existing fields in the object
the list class, which holds all the methods for list objects (like the push method), and also serves as a constructor for list objects
a metatable (containing a __call field) for the list class, so that you can call the list table as if it were a function
As metatable fields always start with two underscores (__), and normal methods usually don't, you can put metatable fields and normal methods side by side into a single table without conflict. And this is what happened here. The list class table also serves as metatable for list objects. So using this trick you can save the memory you would normally need for the separate metatable (the size in bytes for Lua 5.2 on an x86-64 Linux is shown in square brackets in the table title bars, btw.):
Does Window.mt simply create a table?
No, {} creates a table. However, this new table is saved under key "mt" in the Window table, probably to give users of this Window "class" direct access to the metatable that is used for window objects. Given only the code you showed this is not strictly necessary, and you could have used a local variable instead.
Why do we need Window = {} as a namespace here?
In principle, you could store Window.mt, Window.new, and Window.prototype separately, but that would get cumbersome if you have multiple "classes" like Window. This way you can avoid name clashes, and using the Window "class" looks nicer.
Another reason might be that require can only return a single value from a module definition, and if you want to export multiple values (like new, mt, and prototype) from a module, you need a table to wrap them together (or use global variables, but that is considered bad style).
Related
local a = {'1'}
b = {'2'}
print('--------a:', a) --------a: table: 002411A0
print('--------b:', b) --------b: table: 005BC470
how can I get like: a.lua:1 in table a
or a.lua:2 in table b
while I know the table address (002411A0)
my lua environment is lua5.1, I don't know if I need to read the source or compiled of lua5.1?
If you are prepared to declare your tables with a helper function, called, say logger, you can achieve your goal.
The idea is to record the line in a virtual table field __line. In the example below I do it using __index metamethod, but you can simply add a field to the created table.
The line number is obtained by debug.getinfo (2).currentline. The choice of 2 is determined by the call stack depth in my example.
local function logged (t)
local line = debug.getinfo (2).currentline
return setmetatable (t, {
__index = function (_, key)
if key == '__line' then
return line
end
end
})
end
local a = logged {'1'}
print (a.__line) -- 12
b = logged {'2'}
print (b.__line) -- 14
There is no way to get a line where the table is defined by using its address (as the line in the source code and the address in memory have no relationship). What you can do is to parse the source code of your script and find where the definition of the table is, but I'm not sure what use it's going to have for you. Maybe you can describe what you are trying to do?
If you indeed want to find where the table is defined, you can use something like metalua that builds abstract syntax tree (AST) of your code fragment that you can then traverse to find where a particular table is defined.
Another option is to parse the output of luac compiler, which will allow you to find what line the NEWTABLE command for a particular table is on.
I'm adding a string to a table in lua. When I use the table in a function the original table is getting altered. I'm only a beginner but I thought that the function could not do that because it is outside of it's scope. Is there something obvious I'm missing?
local testTable= {}
testTable.name = {}
testTable.name[1] = "Jon"
print(testTable.name[1])
local function testFunc(a)
a.name[1] = "Bob"
end
local newTable = testTable
testFunc(newTable)
print(testTable.name[1])
I expected the output to be:
Jon
Jon
The actual output is:
Jon
Bob
How can the testFunc change the testTable?
You assign testTable's address to newTable, so testTable and newTable point to the same table.
If you want to output be
Jon
Jon
You should copy the table when you assign newTable.
You can copy the table like this function:
function table.copy(old)
local new = {}
for k, v in pairs(old) do
new[k] = v
end
return new
end
When I use the table in a function the original table is getting altered. ... I thought that the function could not do that because it is outside of it's scope.
Local variables have their own scope, but tables do not. Two things to remember:
Variables store references, not values. (This only makes a difference for mutable values.)
Tables are mutable, i.e., they can be changed internally.
Breaking it down:
local newTable = testTable
In this line, you're assigning one variable to another, so both variables refer to the same table.
We mutate a table by assigning to an index within that table, so testFunc alters whatever a (actually a.name) refers to. This is handy, because it allows us to write functions that mutate tables that we pass as arguments.
The following function does nothing, like you would expect, because it assigns a new table to the bare name a (which happens to be a local variable):
local function doNothing(a)
a = {name = {'Bob'}}
end
I got a question on how to use dynamic variable names with the table.insert function in Lua.
I want to create some tables with dynamic variable names and afterwards access those tables using table.insert function to fill them with values, however, I am unclear on how to access the newly created table inside of the table.insert function.
My code so far looks like this:
local attributeNames = {"attribute1","attribute2","attribute3"}
local attributes = {}
for k, v in pairs(attributeNames) do
// If attribute name equals name of type then create a new table with
that type name
if(string.match(v, type)) then
attributes[v] = {}
currentAttribute = v
break
end
end
// Insert values into the table with that type name, here I do not get how to call
the table of name "attribute1" for example to fill it with values
table.insert (attributes[currentAttribute], values)
Any help is highly welcome! :)
In lua, now I want to change field name of a table, like
in test1.lua
local t = {
player_id = 2,
item_id = 1,
}
return t
in test2.lua
local t = require "test1"
print( t.item_id )
if I want to change field name item_id -> item_count of t,
I need to use the application like ultraedit, find out all lua file contain item_id, and modified one by one, such modification easy correction or change of leakage, is there any tools can be more easily modified the field name?
If I understand your problem correctly:
(What if item_id also happens to be a field in a different table?)
In my view, there is no generic solution for this task without actually interpreting the script. A field name (e.g., item_id) could appear in many places but be referring to different tables. To make sure you change only references to the correct table you need to actually interpret the script. Given the dynamic nature of Lua scripts, this is not a trivial task.
Using an editor (or preferably a Lua script) to globally replace all occurrences of one name to another will only work if you're certain the 'old' name has only a single context.
A run-time workaround might be to add some metatable to keep both the new and the old name. I think you need to play with the __index and __newindex events. Then, the same field will be accessed by either 'old' or 'new' name.
Example for the metatable trick (only field 'one' is actually part of the table, field 'two' access is made possible via the metatable to read/write field 'one' so that reading from 'two' reads 'one', while writing to 'two' writes to 'one'):
t = {}
t.one = 1
setmetatable(t,{__index = function(t,k)
if k == 'two' then return t.one end
end,
__newindex = function(t,k,v)
if k == 'two' then t.one = v end
end
})
print(t.one,t.two)
t.two = 2
print(t.one,t.two)
I'd like to create a custom contains method on Lua's table data structure that would check for the existence of a key. Usage would look something like this:
mytable = {}
table.insert(mytable, 'key1')
print(mytable.contains('key1'))
Thanks.
In Lua you cannot change ALL tables at once. You can do this with simpler types, like numbers, strings, functions, where you can modify their metatable and add a method to all strings, all functions, etc. This is already done in Lua 5.1 for strings, this is why you can do this:
local s = "<Hello world!>"
print(s:sub(2, -2)) -- Hello world!
Tables and userdata have metatables for each instance. If you want to create a table with a custom method already present, a simple table constructor will not do. However, using Lua's syntax sugar, you can do something like this:
local mytable = T{}
mytable:insert('val1')
print(mytable:findvalue('val1'))
In order to achieve this, you have to write the following prior to using T:
local table_meta = { __index = table }
function T(t)
-- returns the table passed as parameter or a new table
-- with custom metatable already set to resolve methods in `table`
return setmetatable(t or {}, table_meta)
end
function table.findvalue(tab, val)
for k,v in pairs(tab) do
-- this will return the key under which the value is stored
-- which can be used as a boolean expression to determine if
-- the value is contained in the table
if v == val then return k end
end
-- implicit return nil here, nothing is found
end
local t = T{key1='hello', key2='world'}
t:insert('foo')
t:insert('bar')
print(t:findvalue('world'), t:findvalue('bar'), t:findvalue('xxx'))
if not t:findvalue('xxx') then
print('xxx is not there!')
end
--> key2 2
--> xxx is not there!