lua metatables - first parameter in __index function - lua

I'm trying to learn metatables in Lua and I came across the following example: -
local my_metatable = {}
local my_tab = {}
setmetatable(my_tab, my_metatable)
-- Set the __index metamethod:
my_metatable.__index = function (tab, key)
print("Hello, " .. key)
return "cruel world"
end
-- Trigger the __index metamethod:
print("Goodbye, " .. my_tab["world"])
The result is:-
Hello, world
Goodbye, cruel world
My question is - what does the variable tab do, in my_metatable.__index = function (tab, key). I can change it to anything and it doesn't affect the program in any way.
Thanks!
;^)
Zalokin

The tab parameter is passed an argument of the table itself.
For example, given your code my_tab["world"], the parameters tab and key will be passed the arguments my_tab and "world" respectively. Because you didn't use the table in your __index function, it didn't affect anything.
Here is a basic example of what it might be used for. Let us consider a special Array table that acts like an array but has some additional information:
Array = {
length = 0,
array = {}
}
mt = {
__index = function(tab, index)
return tab.array[index]
end
}
setmetatable(t, mt)
--now when Array[3] is written, what will actually be returned is Array.array[3]
print(Array[3]) --will actually print Array.array[3]
This isn't actually the best way to implement this functionality, but hopefully this gives you an idea of why the tab parameter exists and what __index can be used for as a result.

Related

Why doesn't the __call metamethod work in this Lua class?

in the code below, i set up a __call metafunction which, in theory, should allow me to call the table as a function and invoke the constructor, instead of using test.new()
test = {}
function test:new()
self = {}
setmetatable(self, self)
--- private properties
local str = "hello world"
-- public properties
self.__index = self
self.__call = function (cls, ...) print("Constructor called!") return cls.new(...) end
self.__tostring = function() return("__tostring: "..str) end
self.tostring = function() return("self:tstring(): "..str) end
return self
end
local t = test:new()
print(t) -- __tostring overload works
print(tostring(t)) -- again, __tostring working as expected
print(t:tostring()) -- a public call, which works
t = test() -- the __call metamethod should invoke the constructor test:new()
output:
> __tostring: hello world
> __tostring: hello world
> self.tostring(): hello world
> error: attempt to call global `test` (a table value) (x1)
(i'm using metatable(self, self) because i read somewhere it produces less overhead when creating new instances of the class. also it's quite clean-looking. it may also be where i'm getting unstuck).
You're setting the __call metamethod on the wrong table - the self table - rather than the test table. The fix is trivial:
test.__call = function(cls, ...) print("Constructor called!") return cls.new(...) end
setmetatable(test, test)
After this, test(...) will be equivalent to test.new(...).
That said, your current code needs a refactoring / rewrite; you overwrite the implicit self parameter in test:new, build the metatable on each constructor call, and don't even use test as a metatable! I suggest moving methods like tostring to test and setting the metatable of self to a metatable that has __index = test. I'd also suggest separating metatables and tables in general. I'd get rid of upvalue-based private variables for now as they require you to use closures, which practically gets rid of the metatable benefit of not having to duplicate the functions per object. This is how I'd simplify your code:
local test = setmetatable({}, {__call = function (cls, ...) return cls.new(...) end})
local test_metatable = {__index = test}
function test.new()
local self = setmetatable({}, test_metatable)
self._str = "hello world" -- private by convention
return self
end
function test_metatable:__tostring()
return "__tostring: " .. self._str
end
function test:tostring()
return "self:tstring(): " .. self._str
end
If you like, you can merge test and test_metatable; I prefer to keep them separated however.

What does [{n,{}}] do in lua?

As you can tell I'm a beginner in lua. I am trying to understand a function I'm stuck at what the following code segment does?
It is used in the following code snippet in the last line:
function classify(txt_dir, img_dir, cls_list)
local acc = 0.0
local total = 0.0
local fea_img = {}
local fea_txt = {}
for fname in io.lines(cls_list) do
local imgpath = img_dir .. '/' .. fname .. '.t7'
local txtpath = txt_dir .. '/' .. fname .. '.t7'
fea_img[#fea_img + 1] = extract_img(imgpath)
fea_txt[#fea_txt + 1] = extract_txt(txtpath)
end
for i = 1,#fea_img do
-- loop over individual images.
for k = 1,fea_img[i]:size(1) do
local best_match = 1
local best_score = -math.huge
for j = 1,#fea_txt do
local cur_score = torch.dot(fea_img[i][{k,{}}], fea_txt[j])
From my understanding, fea_img is a lua table. Is the line fea_img[i][{k,{}}] some sort of slicing for the value for the key 'i' in the table fea_img?
I tried searching for more examples and found this being used here too (last line):
for i = 1,nsamples,batchsize do
-- indices
local lasti = math.min(i+batchsize-1,nsamples)
local m = lasti - i + 1
-- k-means step, on minibatch
local batch = x[{ {i,lasti},{} }]
Any help on this would be really appreciated. Thank you!
In lua you can access a specific index on a table in multiple ways. Like these two examples
local myValue = someTable.theIndex
-- or
local myOtherValue = someTable[2]
So the construct you see here is to access some values from a (nested) table.
Also in lua you can use anything except nil as a index, so even tables are possible.
The line
fea_img[i][{k,{}}]
Can be extended to this:
local index1 = i -- i in this case is your loop variable
local index2 = { k , { } } -- This creates a table with 2 values, the first one will be the vaule of the var k, the second one is an empty table
local value1 = fea_img[index1] -- This will get you a table
local value2 = value1[index2] -- This will get the same as: fea_img[i][{k,{}}]
Correction and Addition:
As Nicol Bolas already said in the comments: The index must be an exact match. Which means it literally has to be the same table, which is not the case for the presented code from you. Either you dropped code you thought is unnecessary or fea_img has some some kind of metatable on it.
In the case of
local k = 2
local table1 = {k, { } }
local table2 = {k, { } }
table2 and table1 do have the exact same content. But they are not the same table. Which will lead to nil always being retrieved if one is used to store data in a table and the other is used to get it back.
Syntactically, t[k] is indexing a table with a key. Normally, if there is a record in the table with the key k, its value is returned. Nothing more, nothing less.
If fea_img[i] was a normal table, {k,{}} would always return nil, since table indices are resolved based on their identity ({k,{}} is always a new table). Based on your code, I have to conclude that the elements of fea_img (i.e. what extract_img returns) are not normal tables.
In Lua, you can override the indexing operation using a metatable. If you index a value that has a metatable with __index, it will be used if there is no matching record in the table:
local t = {}
setmetatable(t, {
__index = function(t, k)
return k
end
})
print(t[{}])
This table has a metatable associated with it, which is used in the indexing operation. In this case __index returns the key, but whatever library you are using might provide more complex behaviour.
This is specific to the library you are using, not something related to the Lua syntax.

Accessing the key in the value (right side) of a table key affectation in Lua

The goal is to match a key of a table with a value depending of the key.
example = { ["dummy"] = this .. " example" }
print example.dummy -- print "dummy example"
Where this is the keyword to refer to the key. Is there any way to do that in Lua?
There is no way to do this directly.
You could do some preprocessing:
example = { ["dummy"] = "{THIS} example" }
for k,v in pairs(example) do
example[k]=v:gsub("{THIS}",k)
end
print(example.dummy)
It's not possible to have as clean an expression as:
t = { foo = this .. ' bar' }
because this would always be expressed without relation to the key or the table. That is, you can't capture an expression as the value of a table entry.
What is possible is implementing some level of indirection using metatables and functions, but it's hardly pretty. Here we do fetch-time evaluation. You could also recompute the results.
local function indirect_table ()
local uptable = {}
return setmetatable({}, {
__index = function (self, key)
local value = uptable[key]
return type(value) == 'function' and uptable[key](key) or value
end,
__newindex = function (self, key, value)
uptable[key] = value
--[[precompute, with no need for an uptable, or __index:
`rawset(self, key, value(key)`]]
end
})
end
local tab = indirect_table()
tab.foo = function (key) return key .. 'bar' end
print(tab.foo) --> 'foobar'
Note: this example uses a closure, but you can implement this kind of pattern using getmetatable as well.
Personally, I'd abstract this into an indirection pattern that allows arbitrary keys and values, and their actions to be specified. I figure this kind of pattern would mostly be used programatically, rather than by hand, where the results of key values are dependent on the inputs received. Again, not pretty, but a little more robust (optional actions).
local function I (_, value) return value end
local K = setmetatable({
__call = function (actor, key)
return actor.action(key, actor.value)
end
}, {
__call = function (K, value, action)
return setmetatable({ value = value, action = action or I }, K)
end
})
local T = setmetatable({
__newindex = function (self, key, value)
if getmetatable(value) == K then
value = value(key)
end
rawset(self, key, value)
end
}, {
__call = function (T, o)
return setmetatable(o or {}, T)
end
})
Simple use:
local function concat (left, right) return left .. right end
local t = T {}
t.foo = K('bar', concat) -- with a common action
t.zar = K({}, unknown_action) -- without action (`nil`)
t.qux = 'qaz' -- standard
print(t.foo, t.zar, t.qux)
This is strange metaprogramming. I'd double-check the reasoning for needing such an approach. Perhaps you're falling into an XY Problem trap? Really feels like a solution to a problem that doesn't need to exist in the first place.

How "self" gets to access the key of a local table?

I am new to Lua, I am studying this video. At 38:56, a code is presented as below:
Sequence = {}
function Sequence:new()
local new_seq = {last_num = 0}
self.__index = self
return setmetatable(new_seq, self)
end
function Sequence:next()
print(self.last_num)
end
My understanding is that self is equivalent to Sequence, and self is set as a metatable of new_seq, and this metatable's __index is also self.
And last_num is one key of table new_seq but not one key of self, how come in the definition of next function you can write self.last_num as treating last_num is one key of self?
Moreover, before calling setmetatable, there is self.__index = self, I thought only metatable has __index as a special key, but before calling
setmetatable, Sequence is just a regular table and it is not a metatable yet, how come it has __index?
Short version:
My understanding is that self is equivalent to Sequence…
self is an implicit argument resulting from the method-style function definition. Within the function, it will refer to whatever value gets passed in as the first argument to that function.
(The rest of your questions around self arise from that same confusion.)
I thought only metatable has __index as a special key…
A metatable is just a table. __index is just a key like any other, you can define a field with that name on any table. Only when a lookup on a table fails and Lua notices that this table has an attached metatable, the metatable's field named __index has a special meaning – because that's where Lua will look for a handler.
__index containing a table is just another special case of a handler (because it's so common), __index = some_other_table is roughly equivalent to __index = function( table, key ) return some_other_table[key] end – i.e. "go look over there in some_other_table if table[key] was empty". (It may help to use the long version and print something from there if you have trouble following what happens.)
Long version, de-sugaring the code and walking through the details:
A definition function foo:bar( ... ) is the same as function foo.bar( self, ... ) (the name self is automatically chosen, roughly like this in other languages). Additionally, function foo.bar( ... ) is the same as foo.bar = function( ... ). Which means the above code is the same as…
Sequence = {}
Sequence.new = function( self )
local new_seq = { last_num = 0 }
self.__index = self
return setmetatable( new_seq, self )
end
Sequence.next = function( self )
print( self.last_num )
end
…which is equivalent to…
Sequence = {
new = function( self )
local new_seq = { last_num = 0 }
self.__index = self
return setmetatable( new_seq, self )
end,
next = function( self )
print( self.last_num )
end,
}
So, in essence, what this defines is a table that contains two functions, each taking a single parameter. The second of the two functions, next, is pretty simple: it just prints the content of the field last_num of whatever table it's passed (using the name self to refer to it).
Now, just as for definitions, there's some :-syntax sugar for calls. A call foo:bar( ... ) translates to foo.bar( foo, ... ), so when you have some_sequence and say some_sequence:next( ), what happens is a call some_sequence.next( some_sequence ) – the :-syntax for definitions introduces an extra hidden parameter and the :-syntax for calls fills in that extra parameter. In this way, the function that you're treating as a method has access to the table that you're treating as an object and everything works out nicely.
The new function is a bit more involved -- I'll rewrite it into yet another equivalent form to make it easier to read:
function Sequence.new( self )
self.__index = self
return setmetatable( { last_num = 0 }, self )
end
So for whatever table gets passed in, it assigns that table to the field __index of that same table and returns a new table with that old table set as the metatable. (Yes, this thing is confusing… don't worry, just keep reading.) To see why and how this works, here's an example:
If you say some_sequence = Sequence:new( ), you'll have the following structure:
some_sequence = { last_num = 0 } -- metatable:-> Sequence
Sequence = { new = (func...), next = (func...), __index = Sequence }
Now, when you say some_sequence:next( ), this translates to the call some_sequence.next( some_sequence ). But some_sequence doesn't have a field next! Because some_sequence has a metatable, Lua goes and looks at that – in this case, the metatable is Sequence. As a lookup (or "index") operation "failed" (it would have returned nil), Lua looks for a handler in the metatable's field __index, finds a table (Sequence again) and re-tries the lookup on that one instead (finding the next function we defined).
Which means in this case we could have equivalently written Sequence.next( some_sequence ) (but in general you don't want to – or can't – manually resolve these references). As described above, next just prints the value of the field last_num of the table it received -- in this case it got some_sequence. Again, everything works out nicely.
Some more remarks (and yet another example):
For an introductory example, the code is much more mind-bending and brittle than necessary. Here's yet another version (that's not identical and actually behaves differently, but should be easier to understand):
Sequence = { }
Sequence.__index = Sequence
function Sequence.new( )
return setmetatable( { last_num = 0 }, Sequence )
end
function Sequence:next( )
print( self.last_num )
end
Both the version that you have and this version will print 0 when you run the following:
some_sequence = Sequence:new( )
some_sequence:next( )
(I've described above what happens under the hood when you do this for your code, compare and try to figure out what happens with my version before reading on.)
This will also print 0 for both versions:
sequences = { [0] = Sequence }
for i = 1, 10 do
local current = sequences[#sequences]
sequences[#sequences+1] = current:new( )
end
local last = sequences[#sequences]
last:next( )
What happens under the hood differs significantly for both versions. This is what sequences will look like for your code:
sequences[0] = Sequence -- with __index = Sequence
sequences[1] = { last_num = 0, __index = sequences[1] } -- metatable:->Sequence
sequences[2] = { last_num = 0, __index = sequences[2] } -- metatable:->sequences[1]
sequences[3] = { last_num = 0, __index = sequences[3] } -- metatable:->sequences[2]
...
and this is what it will look like with my version:
sequences[0] = Sequence -- __index = Sequence, as set at the start
sequences[1] = { last_num = 0 } -- metatable:->Sequence
sequences[2] = { last_num = 0 } -- metatable:->Sequence
sequences[3] = { last_num = 0 } -- metatable:->Sequence
...
(If you'd instead say sequences[#sequences+1] = Sequence:new( ) in the loop above, your code would also produce this.)
With my version, the call last:next( ) fails to find next, looks at the metatable (Sequence), finds an __index field (again, Sequence) and finds next, then proceeds to call it as described above.
With your version, the call last:next( ) fails to find next, looks at the metatable (sequences[9]), finds an __index field (sequences[9]), fails to find next and therefore looks at the metatable (of sequences[9], which is sequences[8]), finds an __index field (sequences[8]), fails to find next and therefore looks at the metatable ... (until we reach sequences[1]) ... fails to find next, looks at the metatable (Sequence), finds an __index field (Sequence) and finally finds next, then proceeds with the call. (This is why I said it's quite hard to follow...)
The code that you have implements prototype-based OOP, with all the pros and cons. As you've seen, the lookup traverses the whole chain, which means that you could define a function sequences[5].next to do something else and subsequently sequences[5] through sequences[10] would find that other function. This can be really useful – no need for all the boilerplate to define a new class to change some functionality, just adjust one object and use it like a class. (This can also be annoying if you accidentally do this.)
My version implements something a bit closer to the class-based OOP seen in many other languages. (You can't accidentally override methods for more than one object at once.) What both of these (and many other approaches to OOP in Lua) have in common is that defining a field of an object with the same name as a method will hide that method and make it inaccessible. (If you define some_sequence.next, saying some_sequence:next( ) or some_sequence.next( some_sequence ) will immediately find the next you defined and Lua won't bother to look at the metatable and so on.)

How does `table.insert` work with custom tables in Lua

I wonder how does table.insert work in lua?!
I am asking this because I have tried to use it on a custom table with __newindex metamethod
but it seems not to call it. Is there a way to make my custom table functionality to work with table.insert?!
From my humble knowledge about the language I would say it uses something like rawset or something maybe I donno.
Sample I worked on:
do
tabl = {1,2,3}
local _tabl = tabl
tabl = {}
local mt = { __newindex = function(t,k,v) print"changing" ;_tabl[k] = v end, __index = _tabl}
setmetatable(tabl,mt)
end
tabl[4] = 4; --prints "changing"
table.insert(tabl,5) -- prints nothing!!
There's no such metamethod, table.insert just inserts a new value to a specified table.
local myTable = {}
table.insert(myTable, "somestring")
-- so now myTable has one value, myTable = { "somestring" }
It works like:
local myTable = {}
myTable[#myTable + 1] = "somestring"
__newindex metamethod affects only assignment operator "=", table.insert is just a separate function not related with metatables, you can modify the behaviour of this function if you want:
_tableinsert = table.insert
function table.insert(t, v)
-- here your actions, before real function will be used
_tableinsert(t, v)
end
I think that would be possible to make your own metamethod __tableinsert this way.
table.insert does, in fact, use rawset. See the lua 5.1 source here.
As indicated if you do the assignment yourself you should be able to get the behavior you want.

Resources