Functional object (Matlab)

From LiteratePrograms

Jump to: navigation, search

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
Views
Personal tools