views:

141

answers:

2

Is there a way to "declare" a variable with a particular user-defined type in MATLAB? zeros() only works for built-in numeric types. The only solution I've come up with involves using repmat() to duplicate a dummy object zero times:

arr = repmat(myClass(), [1 0])

Without declaring variables this way, any code which does "arr(end+1) = myClass()" has to include a special case for the default empty matrix which is of type double.

Have I missed something a little more sensible?

+4  A: 

According to this documentation, all classes have an empty method that creates empty arrays of that class. For example:

arr = myClass.empty(0,0);  %# Creates a 0-by-0 array of class myClass

This is also true for built-in types:

a = uint8.empty(0,1);   %# A 0-by-1 uint8 array
b = single.empty(5,0);  %# A 5-by-0 single array
c = cell.empty(0,0);    %# A 0-by-0 cell array


A note on preallocation...

You mentioned that you will be growing this array in a loop in the following way:

arr(end+1) = myClass();

If you know what the final size of the array will be, it is generally more efficient to preallocate the entire array outside of the loop and then overwrite or modify the array elements in your loop. I discuss how you can do this for user-defined classes in an answer to another question.

gnovice
+1  A: 

That's what I use. You can use the slightly terser form that takes a scalar size argument.

r = repmat(MyClass, 0);

Note that you're not declaring the variable to have a type; it's still just the value held in the variable that has a type.

This will work with both old style and new MCOS classes. If you're using all new style classes, gnovice's "empty()" sounds like a good idea.


If you're feeling advanced, there's another option, which I'm including for completeness.

You can also handle this in subsasgn for your objects, at least for old school Matlab class. If you do an indexed assignment into an unitialized variable with a user defined object on the RHS ("right hand side"), subsagn for that class gets invoked, with the LHS coming in as [] (an empty double). If you have a special constructor form that allows you to construct an empty object without calling repmat on the object, you can support this so users do not have to preallocate their variables with objects of your class.

In your subsasgn:

function obj = subsasgn(obj, S, B)
...
s = S(1);
...
switch s.type
    case '()'
        % Handle dispatch on LHS autovivification
        if isnumeric(obj) && isa(B, mfilename('class'))
            % Must use special ctor to preallocate
            obj = feval(class(B), mxdims(size(B)));
        end

Then in your constructor, have a backdoor calling form for constructing empties by blessing pre-repmatted structs.

function MyClass(varargin) %constructor

SuperClasses = { }; % if you inherit, fill this in

if nargin == 1 && isa(varargin{1}, 'mxdims')
   % special backdoor to support preallocation without repmat
   s = repmat(DataStructure, msize(varargin{1})); % built-in repmat called on plain struct
   out = class(s, mfilename, SuperClasses{:});
   return;
end
...

The @mxdims class is a special class you'll need to create that holds a size vector and serves as a marker to invoke this backdoor form. The msize() method returns the size vector it represents.

If you define MyClass so it supports this, then you can just do "s(1) = MyClass" without preallocating s. You can't do "s(end+1)" though; "end" only works with preallocated values.

This is a tricky area of Matlab to be working in. Working in subsasgn and the type system like this can produce lots of subtle errors, including segfaults. Doing it this way will get you more "complete" behavior for your user defined objects. But it involves work and brittleness, and you're probably better off sticking with "repmat(class, 0)" or "empty()".

Andrew Janke

related questions