« Earlier6 items total Later »

Matlab 2008a

I don't do much Matlab these days but it is nice to see that release 2008a is out with the much waited for object oriented additions.

It is definitely something that will make programming Matlab much easier for larger applications and do away with many of the hacks which I have illustrated myself on this website. However I am not sure I like the verbosity of the OO features.

For example there are provisions for

- attributes
- events
- methods
- listeners

whereas in Ruby you can make do with just instance variables and methods, the rest being sorted out with meta programming tricks. Mentioning metaprogramming I don't see any facility for this within the documentation though it is probably there under the hood.

Also not sure why you are restricted to creating classes in files. It would be neat and generally usefully to be able to create classes at the command line and/or multiple classes per file as is possible in Ruby/Python.

I also noted a boo boo in the documentation. Some doc writer claims that Matlab is weakly typed, when in fact they mean dynamically typed as opposed the to statically typed.

Matalb is strongly and dynamically typed whereas C/C++ is weakly and statically typed.

A simple example from the Mathworks home page


classdef BankAccount < handle
   properties (Hidden)
      AccountStatus = 'open';
   end
% The following properties can be set only by class methods
   properties (SetAccess = private)
      AccountNumber
      AccountBalance = 0;
   end
% Define an event called InsufficientFunds
   events
      InsufficientFunds
   end
   methods
      function BA = BankAccount(AccountNumber,InitialBalance)
         BA.AccountNumber = AccountNumber;
         BA.AccountBalance = InitialBalance;
% Calling a static method requires the class name
% addAccount registers the InsufficientFunds listener on this instance
         AccountManager.addAccount(BA);
      end
      function deposit(BA,amt)
         BA.AccountBalance = BA.AccountBalance + amt;
         if BA.AccountBalance > 0
            BA.AccountStatus = 'open';
         end
      end
      function withdraw(BA,amt)
         if (strcmp(BA.AccountStatus,'closed')&& BA.AccountBalance < 0)
            disp(['Account ',num2str(BA.AccountNumber),' has been closed.'])
            return
         end
         newbal = obj.AccountBalance - amt;
         BA.AccountBalance = newbal;
% If a withdrawal results in a negative balance,
% trigger the InsufficientFunds event using notify
         if newbal < 0
            notify(BA,'InsufficientFunds')
         end
      end % withdraw
   end % methods
end % classdef


See Mathworks for more information.

Tree structure implemented with nested functions

function obj = mktree(value)
    _children = [];
    _parent   = [];
    _value    = v;

    obj.add_child = @add_child;
    obj.children = @children;
    obj.parent = @parent;
    obj.each = @each;

    function child = add_child(value)
        child = mktree(obj, value);
        _children = [ _children child ];
    end

    function ch = children
        ch = _children;
    end

    function p = parent
        p = _parent;
    end

    function v = value
        v = _value;
    end

    % Depth first iteration
    function each( fn )
        for i = 1:length(_children)
            c = _children(i);
            c.each(fn);
        end
        fn(c);
    end

end


%% Usage
>> t = mktree(10);
>> c1 = t.add_child(20);
>> c2 = t.add_child(30);
>> c11 = c1.add_child(40);

% Do a depth first iteration
>> t.each( @(c) disp(c.value()) );
40
20
30
10

Matlab hashtable

Structs in Matlab are very usefull for collecting associated data together. However in Matlab you are limited to using valid Matlab variable names for the fields in the struct, even when using dynamic indexing.

For example.

>> h.('bad name') = 10
??? Invalid field name: 'bad name'.


What matlab requires is a hashtable object, sometimes refered to as a dictionary in other programming language. A hashtable can associate any string (key) with a value and retrieve the value later. More generic hashtables allow keys to be arbitrary objects but for the sake of simplicity I stick to strings for this implementation.

First of all download the hashtable object

Now you can try the following

   h = hashtable
   h.x = 10;
   y = h.x;


That was just like a standard matlab struct but it has other tricks.

h   = hashtable
h.x = 10;
y   = h;
h.x = 20;
y.x

  ans =

     20


Thats right, hashtable is a reference object. Assigning the hashtable instance to another variable does not make a copy of the data.

Lets try to use non standard field names.

h = hashtable
h.('a funny key') = 10;
y = h.('a funny key')

y =

    10


How does all this work?

First of all I am using a combination of nested functions and standard Matlab OOPs objects. The M-OOPS give me the ability to overide subsref and subsasgn to get the funky indexing and the nested functions give me the reference object behaviour.

The storage is managed by generated a hash of the string and using that hash to index into a storage cell array. My hash generating function is

function hash = hashcode(str)
    str = uint32(str(:))';
    hash = uint32(5381);
    for c = str
        hash = bitshift(hash, 5) + hash + c;
    end
end


I have a C version of the above function but for portability it is easier to use pure Matlab code. Unfortunately the pure M code is about 10 times slower in generating the code.

Once we have the code we generate a number over the number of storge buckets we have available.

 function i = hash(string)
      i = rem(hashcode(string),buckets) + 1;
 end


Retrieving the value is fairly simple. The modified hash is used to retrieve a structure array from a cell array of buckets. Generally the structure array is empty or contains only a few key-value pairs. A linear search is performed on the structure array to get the correct value.

   function out = get(key)
      i = hash(key);
      if isempty(contents{i})
         out = [];
         return;
      end
      items = contents{i};
      for j = 1:length(items);item = items(j);
         if isequal(item.key, key)
            out = item.value;
            return;
         end
      end
      out = [];
      return;
   end


Putting is more complex. We wish to maintain the efficiency of the table. This means avoiding filling each bucket up too much so that the linear search part takes too much time. If a certain threshold is reached then the number of buckets is increased and the elements redistributed over the hashtable. This is an expensive operation so you try to set up your hashtable with a suitable initial capacity to start with.

Have a look at the code for more details.

How to use nested functions to build an object oriented framework.

This currently only works in Matlab R14 SP3. In previous versions this does not work.

It is sometimes usefull to be able to obtain a function handle of a nested function in the calling scope. What this means is that you use evalin('caller',...) to perform an operation which retrieves function handles from the caller. An example of this is my unit test tool for Matlab MUnit which obtains a list of function handles whose names match the regular expression 'test_[A-Za-z0-9_]*'.

To do this first create a cell array of the function names you wish to grab. How you decide on what these names are are an application matter. In MUnit I scan the calling file and use regular expressions to find all function names that match the patterns.

In munit_testcase.m you will find

 stk = dbstack('-completenames');
 mname = stk(2).file;
 fcn_names = scan(mname, {'setup' ,'teardown', 'test_[A-Za-z0-9_]*'});



where the scan function is defined as

% SCAN
%
% Scans and extracts function names from an m file
% using regular expression patterns.
%
% Arguments
%   file        -   full path to an m file to scan
%   patterns    -   A cell array of regular expressions for function names
%                   to extract
%
function names = scan(fname, patterns)
    if iscell(patterns)
        patterns = sprintf('(%s)|',patterns{:});
    end
    str = evalc(sprintf('mlint(''-calls'',''%s'')', fname));
    names = regexp(str,'\d+\s*([AZa-z][A-Za-z0-9_]*)','tokens');
    names = cellfun(@(x) x{1}, names, 'uniformoutput', false);
    i = cellfun(@(x)~isempty(regexp(x, patterns)), names);
    names = names(i);
end

The above is different to what is in MUnit which uses an older scanning method and caching approach to avoid scanning the file more than once.

When you have your cell array, here stored in the variable fcn_names execute the following code

 fcns = evalin('caller',['@(){' sprintf('@%s ', fcn_names{:}) '}']);
 fcns = fcns();
 for i = 1:length(fcns);fcn = fcns{i};
   test_case.(fcn_names{i}) = fcn;
 end


If fcn_names contains

{'fun1' 'fun2' 'fun3'}


then the following expression will be evaluated in the caller

@(){ @fun1 @fun2 @fun3 }


That is it generates an anonymous function that when evaluated, line 2, generates a cell array of function handles.

Line 3 to line 5 just copies the function handles to named fields on a structure which can be returned.

You may ask why I need the extra layer of the anonymous function. I don't really know. It should be enough just to evaluate the cell array of function handles in the caller but this just does not work. Matlab bug or feature???

The savvy among you may realize that you can use this trick to easily create an object framework in Matlab. For example my theoretical object function may look like.

function self = my_object(x)
   self = create_object;
   function y = m_incr(r)
      x = x + r;
      y = x;
   end
   function y = m_decr(r)
      x = x - r;
      y = x;
   end
end


and create_object looks like
function self = create_object
 stk = dbstack('-completenames');
 mname = stk(2).file;
 fcn_names = scan(mname, {'m_[A-Za-z0-9_]*'});
 fcns = evalin('caller',['@(){' sprintf('@%s ', fcn_names{:}) '}']);
 fcns = fcns();
 for i = 1:length(fcns);fcn = fcns{i};
   self.(fcn_names{i}) = fcn;
 end
end


Now at the command line you should be able to

>> x = my_object(10);
>> x.m_incr(1)
ans =
      11
>> x.m_decr(2)
ans =
      9


Have fun

Simple objects with nested functions

You can create simple reference objects using nested functions and matlab structures.

function obj = counter(c1)

   obj.decr = @decr;
   obj.incr = @incr;

   function c1 = incr
       c1 = c1 + 1;
   end

   function c1 = decr
       c1 = c1 - 1;
   end

   function c1 = get
   end

end


You can then use the returned structure to access the counter
object

% create the counter
c = counter(10);

% increment;
c.incr();
c.incr();

% decrement;
c.decr();

% get the value
c.get()
ans =
     11

Creating reference objects

You will notice some strange notation in some of the Matlab toolboxes, schema.this, schema.that. It pretty obvious this is some rudimentry class and object system for creating reference objects. However it is not documented or supported by The Mathworks so it is probably not a good idea going overboard in using any of it in your projects.

However you can use a small set of these commands to create reference objects. Define the following function.

function h = reference

   % Declare the new class
   p = findpackage('xt');
   if isempty(p)
      p = schema.package('xt');
   end
   c = p.findclass('reference');
   if isempty(c)
      c = schema.class(p, 'reference');
   end
   h = xt.reference;

   %Create a single property 'data'
   prop = schema.prop(h, 'data', 'mxArray');
end


You can then store information in the data field. Unlike standard matlab structure this is a reference structure. So you can do the following.

>> h.data = 10;
>> g = h;
>> h.data = 20;
>> g.data == h.data

ans =

     1


The data field is indexable using any matlab array operators. This means you can pass a reference structure to a function and have it do an in-place operation instead of an expensive copy.

« Earlier6 items total Later »




Sponsored by

Sole Central

Your one stop shop for Birkenstock and Crocs shoes and sandles.