Functional object (Matlab)
From LiteratePrograms
MATLAB allows to use a very simple form of functional object programming through embedded functions. There is a very simple codeblock (see <<firstoo.m>>) in Quantitative Finance section.
Contents |
From embedded functions to functional objects
Embedded functions are functions inside a MATLAB function (between the 'function' and 'end' lines), like in:
<<test_embedded1.m>>= function z = test_embedded1( a) % TEST_EMBEDDED1 - simple test of embedded function direct call of ALPHA function u = ALPHA(v,w) u = v+w; end end
The direct call to ALPHA is:
<<direct call of ALPHA>>= z = ALPHA(a,2);
The use of feval allow to replace the direct call to ALPHA by an indirect one:
<<indirect call of ALPHA>>= z = feval(@ALPHA,a,2);
but more than that, our function can return a handle to alpha:
<<handle to ALPHA>>= z = @ALPHA;
To use such a call to ALPHA, we need an example:
<<test_embedded2.m>>= function z = test_embedded2( a) % TEST_EMBEDDED2 - simple test of embedded function handle to ALPHA function u = ALPHA(v,w) u = v+w; end end
And a script:
<<script_to_test_2.m>>= z = test_embedded2(1); z(2,3)
But the best call to try is:
<<test_embedded3.m>>= function z = test_embedded3( a) % TEST_EMBEDDED3 - simple test of embedded function z = @ALPHA_A; function u = ALPHA_A(x) u = ALPHA(a,x); end function u = ALPHA(v,w) u = v+w; end end
which can be used this way:
<<script_to_test_3.m>>= z = test_embedded3(1); z(2) z(3)
It's very usefull: MATLAB decided to keep the parameter a=1 into its memory. It provides a very simple way to implement objects in MATLAB:
<<functional_object.m>>= function z = functional_object( v1, ..., vN) % FUNCTIONAL_OBJECT - this = struct('v1', v1, ..., 'vN', vN); z = struct('get_v', @get_v, 'set_v', @set_v, etc); function f = get_fields f = fieldnames(this); end function v = get_v(n) f = get_fields(); v = this.(f{n}); end function set_v(n, v) f = get_fields(); this.(f{n}) = v; end ... end
Here we decided to publish methods set_v and get_v only (keeping get_fields as an internal method), and the data contained in the object can only be read or write through public methods.
Inheritance
Now it's easy to have heritage with such objects:
First object.
<<object1.m>>= function z = object1( v) % OBJECT1 - addition this.v = v; z = struct('add', @ADD, 'get', @get, 'set', @set); function v = set(v) this.v = v; end function v = get v = this.v; end function u = ADD( x) u = x+this.v; end end
Second object.
<<object2.m>>= function z = object2( v) % OBJECT2 - substraction this.v = v; z = struct('substract', @SUBSTRACT, 'get', @get, 'set', @set); function v = set(v) this.v = v; end function v = get v = this.v; end function u = SUBSTRACT( x) u = x-this.v; end end
inherited one.
<<object12.m>>= function z = object12( v) % OBJECT12 - addition and substraction this = struct('v', v, 'add', object1(v), 'substract', object2(v)); z = struct('substract', this.substract.substract, 'add',this.add.add, ... 'get', @get, 'set', @(x)([set(v), this.add.set(v), this.substract.set(v)])); function v = set(v) this.v = v; end function v = get v = this.v; end end
The important point is that it can be automatized:
<<inherit.m>>= function z = inherit( objects, varargin) % INHERIT - automated inheritance this = struct('value', {varargin}, 'objects', {objects}); for o=1:length(objects) this.(objects{o}) = feval(objects{o}, varargin); end z = struct('get', @get_this, 'invoke', @hinvoke); function [v, os] = hinvoke( method_name, varargin) v = {}; os = {}; % For each parent object for o=1:length(this.objects) % Find existing methods with this name idx = strmatch(method_name, fieldnames(this.(objects{o})), 'exact'); if ~isempty(idx) % If possible, apply it (and store the name of the used object) os{end+1} = this.objects{o}; if isempty(varargin) v{end+1} = feval(this.(this.objects{o}).(method_name)); else v{end+1} = feval(this.(this.objects{o}).(method_name), varargin); end end end end function t = get_this t = this; end end
It can be used this way:
<<use_inheritance_script.m>>= z = inherit({'object1', 'object2'}, 2); z.get() z.invoke('set', 3); z.invoke('get'); z.get() z.invoke('add',1) z.invoke('substract',1)
Polymorphism
You will have to implement youself the polymorphism of your methods, the is a MATLAB function could be usefull for that. For instance:
<<polymorphism_example.m>>= function z = polymorphism_example( v) % POLYMORPHISM EXAMPLE - this.value = v; z = struct('get',@get,'set',@set,'to_string',@to_string); get and set function str = to_string(pref, v) if nargin<1 pref = ''; end if nargin<2 v = this.value; end if isstr(v) str = [pref '-str:' v ':']; elseif isnumeric(v) if length(v)==1 str = [pref '-num:' num2str(v) ':']; else str = [pref '-vnum:' sprintf('%f:',v)]; end elseif isstruct(v) fnames = fieldnames(v); str = ''; for f=1:length(fnames) str = sprintf('%s%s\n', str, to_string(['struct:' fnames{f}], v.(fnames{f}))); end str = str(1:end-1); elseif iscell(v) str = ''; for f=1:length(v) str = sprintf('%s%s\n', str, to_string('cell', v{f})); end str = str(1:end-1); elseif isa(v, 'function_handle') str = '@???'; else str = '***UNKNOWN***'; end end end
Here I have a polymorph to_string method.
The set and get methods are always the same:
<<get and set>>= function v = get v = this.value; end function set( v) this.value = v; end
to test it:
<<script_pe.m>>= pe=polymorphism_example( 5) pe.to_string() pe.set(1:10); pe.to_string() pe.set('garzol'); pe.to_string() pe.set({'garzol', 4}); pe.to_string() pe.set(struct('alpha',{{'garzol', 4}},'beta',1:3)); pe.to_string()
Very light struct object
This may be the generic form of a function which implement at the same time a struct object and a functionnal object in MATLAB.
It is very easy to use:
<<script4mlo.m>>= %% How to build simple MATLAB object %% Functionnal version mlo = my_light_object('init', 'alpha', 1, 'beta', 2) my_light_object('get', mlo, 'alpha') my_light_object('set', mlo, 'beta', 8, 'garzol', 12) %% Struct object version mlo = my_light_object('create', 'alpha', 1, 'beta', 2) mlo.get('alpha') mlo.set('beta', 8, 'garzol', 12) mlo.get()
The methods are here very simple:
- |get| to retrieve one or several attributes
- |set| to set one or more attributes
In *functionnal mode*, you have to give the 'struct' to the function, like in:
>> mlo = my_light_object('init', 'alpha', 1, 'beta', 2) mlo = alpha: 1 beta: 2 >> my_light_object('get', mlo, 'alpha') ans = 1
In *struct object* mode, it's like in traditionnal object programming:
>> mlo = my_light_object('create', 'alpha', 1, 'beta', 2) mlo = get: @my_light_object/get_ set: @my_light_object/set_ >> mlo.set('beta', 8, 'garzol', 12) >> mlo.get() ans = alpha: 1 beta: 8 garzol: 12
<<my_light_object.m>>= function z = my_light_object( mode, varargin) % MY_LIGHT_OBJECT - a light struct object % two versions embedded: % % 1. functional: % mlo = my_light_object('init', 'alpha', 1, 'beta', 2) % my_light_object('get', mlo, 'alpha') % my_light_object('set', mlo, 'beta', 8, 'garzol', 12) % % 2. struct object % mlo = my_light_object('create', 'alpha', 1, 'beta', 2) % mlo.get('alpha') % mlo.set('beta', 8, 'garzol', 12) % mlo.get() switch lower(mode) case 'init' %<* init a function mlo z = []; for i=1:2:length(varargin)-1 z.(varargin{i}) = varargin{i+1}; end %>* case 'get' %<* Get attribute if length(varargin)==1 z = varargin{1}; return end z = {}; for i=2:length(varargin) z{end+1} = varargin{1}.(varargin{i}); end if length(z)==1 z = z{1}; end %>* case 'set' %<* set attribute z = varargin{1}; for i=2:2:length(varargin)-1 z.(varargin{i})= varargin{i+1}; end %>* case 'create' %<* object version this = my_light_object('init', varargin{:}); z = struct('get', @get_, 'set', @set_); %>* otherwise error('my_light_object:mode', 'mode <%s> is unknown', mode); end %%** Internals embedded_methods end
<<embedded_methods>>= %<* Embedded get function v = get_( varargin) v = my_light_object('get', this, varargin{:}); end %>* %<* Embedded set function set_(varargin) this = my_light_object('set', this, varargin{:}); end %>*
Download code |