I want to build a simple inheritance hierarchy in Lua. The BaseClass has two attributes, a single value val and a table vals. If I create two objects foo and bar of the SubClass and change these two attributes, changes of val work as expected, but for vals it seems like both objects share the same table internally.
BaseClass = {}
function BaseClass:new()
o = {}
setmetatable(o, self)
self.__index = self
o.val = 0
o.vals = {}
return o
end
SubClass = BaseClass:new()
function SubClass:new()
o = {}
setmetatable(o, self)
self.__index = self
return o
end
foo = SubClass:new()
bar = SubClass:new()
foo.val = 1
bar.val = 2
foo.vals[#foo.vals + 1] = 1
bar.vals[#bar.vals + 1] = 2
print(foo.val, bar.val)
print(#foo.vals, #bar.vals)
The code prints
1 2
2 2
How can I solve this? How do I create two different tables for foo and bar?
Your new method does not distinguish between subclasses and instances. (I don't know why Programming in Lua does it this way.) One way to solve this is to have a separate method to make subclasses:
BaseClass = {}
BaseClass.__index = BaseClass
function BaseClass:new()
o = {}
setmetatable(o, self)
o.val = 0
o.vals = {}
return o
end
function BaseClass:subclass()
local c = {}
setmetatable(c, self)
c.__index = c
return c
end
SubClass = BaseClass:subclass()
Related
How to inherit a new class player from class actor and make method actor:new(x, y, s) called in method player:new(x, y, s) with the same parameters. I need to make player:new the same but with additional parameters so the player can have more properties than actor.
Is there a way to do this not only in new method, but in other methods, like player:move(x, y) will call actor:move(x, y) or self:move(x, y) but with additional code?
I use this pattern to make classes in modules:
local actor = {}
function actor:new(x, y, s)
self.__index = self
return setmetatable({
posx = x,
posy = y,
sprite = s
}, self)
end
-- methods
return actor
A good way to do this is to have a separate function that initializes your instance. Then you can simply call the base classes initialization within the inherited class.
Like in this example: http://lua-users.org/wiki/ObjectOrientationTutorial
function CreateClass(...)
-- "cls" is the new class
local cls, bases = {}, {...}
-- copy base class contents into the new class
for i, base in ipairs(bases) do
for k, v in pairs(base) do
cls[k] = v
end
end
-- set the class's __index, and start filling an "is_a" table that contains this class and all of its bases
-- so you can do an "instance of" check using my_instance.is_a[MyClass]
cls.__index, cls.is_a = cls, {[cls] = true}
for i, base in ipairs(bases) do
for c in pairs(base.is_a) do
cls.is_a[c] = true
end
cls.is_a[base] = true
end
-- the class's __call metamethod
setmetatable(cls, {__call = function (c, ...)
local instance = setmetatable({}, c)
-- run the init method if it's there
local init = instance._init
if init then init(instance, ...) end
return instance
end})
-- return the new class table, that's ready to fill with methods
return cls
end
You would simply do:
actor = CreateClass()
function actor:_init(x,y,s)
self.posx = x
self.posy = y
self.sprite = s
end
player = CreateClass(actor)
function player:_init(x,y,s,name)
actor.init(self, x,y,s)
self.name = name or "John Doe"
end
I'm learning how to use Lua and Love2d and I want to create a Vec2 class using metamethods and metatables. This is what I have so far:
class.lua: (The base class files)
local Class = {}
Class.__index = Class
-- Constructor
function Class:new() end
-- Inherite from Class
-- type = The name of the new class
function Class:derive(type)
print("Class:", self)
local cls = {}
cls["__call"] = Class.__call
cls.type = type
cls.__index = cls
cls.super = self
setmetatable(cls, self)
return cls
end
function Class:__call(...)
local inst = setmetatable({}, self)
inst:new(...)
return inst
end
function Class:getType()
return self.type
end
return Class
vec2.lua
local class = require "class"
local Vec2 = class:derive("Vec2")
function Vec2:new(x, y)
self.x = x or 0
self.y = y or 0
getmetatable(self).__add = Vec2.add
end
function Vec2.add(a, b)
local nx, ny
nx = a.x + b.x
ny = a.y + b.y
return Vec2:new(nx, ny)
end
return Vec2
and in my main.lua I have:
local v1 = Vec2:new(10, 10)
local v2 = Vec2:new(5, 3)
local v3 = v1 + v2
print("v3:", v3.x, v3.y)
and I get this error:
Error: main.lua:12: attempt to perform arithmetic on local 'v1' (a nil
value)
Vec2.new does not return a value. Hence the assignment local v1 = Vec2:new(10,10) results in v1 being nil
Try local v1 = Vec2(10,10) instead. Same mistake in Vec2.add
The instance is created in the __call metamethod which calls new with your parameters. You're not supposed to call new directly unless you want to re-initialize an existing instance.
function Class:__call(...)
local inst = setmetatable({}, self)
inst:new(...)
return inst
end
I want to create a subclass, ess say, to the built-in ss class. I'd like to be able to convert an existing ss object to an ess object and at the same time add the missing properties, e.g. w, by something like this
sys=ss(a,b,c,d);
esys=ess(sys,w);
but I can't figure out how to setup the constructor correctly. What is the best way to achieve this? My code currently looks like this
classdef ess < ss
properties
w
end
methods
function obj = ess(varargin)
if nargin>0 && isa(varargin{1},'StateSpaceModel')
super_args{1} = sys;
else
super_args = varargin;
end
obj = obj#ss(super_args{:});
end
end
end
But this does not work as I get the following error:
>> ess(ss(a,b,c,d))
??? When constructing an instance of class 'ess', the constructor must preserve
the class of the returned object.
Of course I could copy all the object properties by hand but it seems to me that there should be some better way.
Here is an example of what I had in mind:
classdef ss < handle
properties
a
b
end
methods
function obj = ss(varargin)
args = {0 0}; %# default values
if nargin > 0, args = varargin; end
obj.a = args{1};
obj.b = args{2};
end
end
end
and:
classdef ess < ss
properties
c
end
methods
function obj = ess(c, varargin)
args = {};
if nargin>1 && isa(varargin{1}, 'ss')
args = getProps(varargin{1});
end
obj#ss(args{:}); %# call base-class constructor
obj.c = c;
end
end
end
%# private function that extracts object properties
function props = getProps(ssObj)
props{1} = ssObj.a;
props{2} = ssObj.b;
end
Lets test those classes:
x = ss(1,2);
xx = ess(3,x)
I get:
xx =
ess handle
Properties:
c: 3
a: 1
b: 2
Methods, Events, Superclasses
Hi I am trying to implement a linked list in Matlab.
The implementation I am hoping to do is (this is the C equivalent):
class Node{
public Node* child;
}
I have looked around but don't seem to get anything quite close.
I guess that you want to implement a linked list:
classdef Node < handle
properties
Next = Node.empty(0); %Better than [] because it has same type as Node.
Data;
end
methods
function this = Node(data)
if ~exist('data','var')
data = [];
end
this.Data = data;
end
end
end
Creation :
n1 = Node('Foo'); %Create one node
n2 = Node('Bar'); %Create another one
n1.Next = n2; %Link between them
Iteration:
n = n1;
while ~isempty(n)
disp(n.Data); %Change this line as you wish
n = n.Next;
end
I have a recursive function fact, which can be called from either an expression inside it or an expression outside it.
I would like to associate fact with a variable v, such that each time fact is called from outside (another function), v is initialized, and its value can be changed inside fact, but never can be initialized when fact is called from inside.
The following code suits my need, but one problem is that v is defined as a global variable, and I have to do v := init before calling fact from outside, which I do not find beautiful.
let init = 100
let v = ref init
let rec fact (n: int) : int =
v := !v + 1;
if n <= 0 then 1 else n * fact (n - 1)
let rec fib (n: int) : int =
if n <= 0 then 0
else if n = 1 then (v := !v + 50; 1)
else fib (n-1) + fib (n-2)
let main =
v := init;
print_int (fact 3);
print_int !v; (* 104 is expected *)
v := init;
print_int (fib 3);
print_int !v;; (* 200 is expected *)
Could anyone think of a better implementation?
You can hide the function and value definitions within the body of a containing function as follows:
open Printf
let init = 100
let fact n =
let rec fact counter n =
incr counter;
if n <= 0 then 1 else n * fact counter (n - 1)
in
let counter = ref init in
let result = fact counter n in
(result, !counter)
let main () =
let x, count = fact 3 in
printf "%i\n" x;
printf "counter: %i\n" count (* 104 is expected *)
let () = main ()
You can adapt Martin's solution so that data is shared across various calls:
let fact =
let counter = ref 0 in
fun n ->
let rec fact = ... in
fact n
The idea is to transform let fact = fun n -> let counter = ... in ... into let fact = let counter = ... in fun n -> ...: counter is initialized once, instead of at each call of fact.
A classical example of this style is:
let counter =
let count = ref (-1) in
fun () ->
incr count;
!count
Beware however that you may get into typing trouble if the function was meant to be polymorphic: let foo = fun n -> ... is always generalized into a polymorphic function, let foo = (let x = ref ... in fun n -> ...) is not, as that would be unsound, so foo won't have a polymorphic type.
You can even generalize the counter example above to a counter factory:
let make_counter () =
let count = ref (-1) in
fun () ->
incr count;
!count
For each call to make_counter (), you get a new counter, that is a function that shares state across call, but whose state is independent from previous make_counter () counter creations.
With Ocaml's objects, you can do:
class type fact_counter = object ('self)
method get : int
method set : int -> unit
method inc : unit
method fact : int -> int
end;;
class myCounter init : fact_counter = object (self)
val mutable count = init
method get = count
method set n = count <- n
method inc = count <- count + 1
method fact n =
self#inc;
if n <= 0 then 1 else n * self#fact (n - 1)
end;;
Then you can obtain:
# let c = new myCounter 0;;
val c : myCounter = <obj>
# c#fact 10;;
- : int = 3628800
# c#get;;
- : int = 11
# c#set 42;;
- : unit = ()
# c#fact 10;;
- : int = 3628800
# c#get;;
- : int = 53
I hope you can easily see how to adapt myCounter to include fib ...