Reputation: 2155
I am trying to implement a Class in Lua which is supposed to be instantiated. However, I am getting an error where the input-parameters to the function BatchLoader.init
are shifted. Am I missing to implement a self
keyword as the argument input to any member function like in python, or is there anything else wrong with the class definitions?
The entire classcode looks like this (I stripped out the unimportant things).:
function BatchLoader:new()
setmetatable({}, BatchLoader)
self.epoch_done = false
return self
end
function BatchLoader:init(X, y, sids, batch_size, argshuffle)
print("In batchloader")
print(X:size()) -- Prints size(62000, 1) [which is 'y']
print(y:size()) -- Prints size(62000, 1) [which is 'sids']
print(sids) -- Prints '10' [which is 'batch_size']
end
But when I create this class, and then call the function BatchLoader.init
it seems that y is interpreted as X, y is interpreted as sids and so on. As such, the input to batch_size
is interpreted as sids
with which every argument is 'shifted away' from the actual parameter.
The following code is what I call right before going into that BatchLoader.init
. Everything here is printed as expected.
local BatchLoader = require "../datahandler/BatchLoader.lua"
local trainLoader = BatchLoader:new()
print(X_data[{{1, 62000}, {}, {}}]:size()) -- Prints size(62000, 8, 16)
print(y_data[{{1, 62000}, {}}]:size()) -- Prints size(62000, 1)
print(sid_data[{{1, 62000}, {}}]:size()) -- Prints size(62000, 1)
local X_train, y_train, sid_train = trainLoader.init(
X_data[{{1, a}, {}, {}}],
y_data[{{1, a}, {}}],
sid_data[{{1, a}, {}}],
10, true)
My question is: Is there anything wrong with the Class-Declaration there? This is my first OOP code in Lua, so any ideas or help is appreciated! :)
Upvotes: 1
Views: 829
Reputation: 7020
In Lua, this syntax:
o:f(x, y)
is syntactic sugar for this:
o.f(self, x, y)
This is true for both definition and invocation. Here you define init
with the colon syntax and call it with the dot syntax, so it won't work because the first argument will become self
and the other arguments will be off by one. A solution is to call init
like this:
local X_train, y_train, sid_train = trainLoader:init(
X_data[{{1, a}, {}, {}}],
y_data[{{1, a}, {}}],
sid_data[{{1, a}, {}}],
10, true)
This solves this issue, but note that in your example the constructor is also completely broken. Because you define it with the colon syntax, it will always return the BatchLoader
table itself and not a new instance. Since it is a class method, you should write it and call it with the dot syntax:
function BatchLoader.new()
local self = setmetatable({}, BatchLoader)
self.epoch_done = false
return self
end
or simply:
function BatchLoader.new()
return setmetatable({epoch_done = false}, BatchLoader)
end
Then you also call it with the dot syntax:
local trainLoader = BatchLoader.new()
To understand those things, if you haven't done it yet, I strongly recommend you buy a copy of the latest edition of Programming in Lua and read the section on Object Oriented programming.
Upvotes: 4
Reputation: 4264
Method syntax (using foo:bar
instead of foo.bar
) automatically introduces a self
parameter.
On function definition, function Foo:bar( ... )
is equivalent to function Foo.bar( self, ... )
. On function call, foo:bar( ... )
is roughly equivalent to foo.bar( foo, ... )
(the latter evaluates foo
twice).
So let's dissect your code:
function BatchLoader:new()
setmetatable({}, BatchLoader) -- creates a table & throws it away
self.epoch_done = false -- self is the hidden first argument
return self
end
local trainLoader = BatchLoader:new() -- i.e. BatchLoader.new( BatchLoader )
which means you're effectively running
function BatchLoader:new()
setmetatable({}, BatchLoader)
BatchLoader.epoch_done = false
return BatchLoader
end
which certainly is not what you want. Several ways to do what you might want are
-- fixed creation scheme
function BatchLoader.new( )
local self = { epoch_done = false }
return setmetatable( self, BatchLoader )
end
-- acceptable uses:
local trainLoader = BatchLoader.new( )
local trainLoader = BatchLoader:new( ) -- ignores passed self
or
-- permit passing base object
function BatchLoader:new( ) -- equivalently: BatchLoader.new( self )
self = self or { } -- use passed table or create new
self.epoch_done = false
return setmetatable( self, BatchLoader )
end
-- acceptable uses:
local trainLoader = BatchLoader.new( ) -- creates from scratch
local trainLoader = BatchLoader.new { n = 23 } -- re-uses this table
-- WRONG uses:
local trainLoader = BatchLoader:new( ) -- reuses BatchLoader class as object
or many other options.
You've equally mixed up the method notation for the other call:
function BatchLoader:init(X, y, sids, batch_size, argshuffle)
is fine and equivalent to
function BatchLoader.init(self, X, y, sids, batch_size, argshuffle)
which you are then wrongly calling as
local X_train, y_train, sid_train =
trainLoader.init( X, y, ... )
(see why everything is shifted?)
Use trainLoader:init( X, y, ... )
to pass trainLoader
as self
.
Upvotes: 3