If I have a value class like this:
classdef MyVal
properties
foo
end
methods
function self = MyVal(varargin)
if nargin > 0
self.foo = varargin{1};
end
end
end
end
and use it in a table:
foo(1) = MyVal(1); foo(2) = MyVal(2);
t = table(foo')
the output is:
t =
2×1 table
Var1
___________
[1×1 MyVal]
[1×1 MyVal]
Is there any method which has to be defined in MyVal (or any property of the table) which allows to change the representation of the value in the table? I do not want to convert the data passed to table since I'd like to retrieve instances of MyVal when I index in the table.
You could create a custom wrapper for table. This is slightly pokey because MATLAB doesn't let you inherit from the table class, but by overloading the subsref and subsasgn operators you can get most of the functionality anyway...
I've included the class at the bottom of this answer since it's a bit long, usage would look like this:
>> foo(1) = MyVal(1); foo(2) = MyVal(2);
>> t = MyTable(foo') % Omitting the ";" calls our overloaded "disp" function
t =
Var1
____
1
2
You can also use this to extract the numeric data (i.e. the foo property alongside other data) by using t.numeric, although I suspect you will need to use table2array on this for use with uitable, as you would with a normal table.
classdef MyTable
properties ( Access = private )
table_
end
properties ( Dependent = true )
numeric
end
methods % Constructor and getter
function obj = MyTable( varargin )
% Store as a normal table internally
obj.table_ = table( varargin{:} );
end
function num = get.numeric( obj )
% By calling obj.numeric, output the table but with MyVal
% objects replaced by their "foo" property.
% This could be passed into a uitable, and is used in disp()
cls = varfun( #(x)isa(x,'MyVal'), obj.table_, 'OutputFormat', 'uniform' );
num = obj.table_;
for ii = find(cls)
num.(num.Properties.VariableNames{ii}) = [num.(num.Properties.VariableNames{ii}).foo].';
end
end
end
methods % Overloaded to emulate table behaviour
function disp( obj )
% Overload the disp function (also called when semi colon is
% omitted) to output the numeric version of the table
disp( obj.numeric );
end
% Overload subsref and subsasgn for table indexing
function varargout = subsref( obj, s )
[varargout{1:nargout}] = builtin( 'subsref', obj.table_, s );
end
function obj = subsasgn( obj, s, varargin )
obj.table_ = builtin( 'subsasgn', obj.table_, s, varargin{:} );
end
% Have to overload size and isa for workspace preview
function sz = size( obj, varargin )
sz = size( obj.table_, varargin{:} );
end
function b = isa( obj, varargin )
% This is only OK to do because we overloaded subsref/subsasgn
b = isa( obj.table_, varargin{:} );
end
end
end
Only a half answer
I couldn't find the way to customise the display of the table of object, but if you can get by by using arrays of object there is a way, described in Customize Object Display for Classes.
For your example, you have to derive your class with: matlab.mixin.CustomDisplay, then override the displayNonScalarObject method:
classdef MyVal < matlab.mixin.CustomDisplay
properties
foo
end
methods
function self = MyVal(varargin)
if nargin > 0
self.foo = varargin{1};
end
end
end
methods (Access = protected)
function displayNonScalarObject(objAry)
dimStr = matlab.mixin.CustomDisplay.convertDimensionsToString(objAry);
cName = matlab.mixin.CustomDisplay.getClassNameForHeader(objAry);
headerStr = [dimStr,' ',cName,' members:'];
header = sprintf('%s\n',headerStr);
disp(header)
for ix = 1:length(objAry)
o = objAry(ix);
% OPTION 1:
% For a class with a sinle property, no need to call the
% big guns, just build your own display:
disp( [num2str(ix) ': foo = ' num2str(o.foo) ] ) ;
% OR OPTION 2: (comment option1 if you use this)
% If your class had multiple properties and you want to
% display several of them, use the MATLAB built-in functions:
% create a structure with the property names you need
% displayed:
% propList = struct('foo',o.foo,'prop2',o.prop2,'prop3',o.prop3);
% Then let MATLAB handle the display
% propgrp = matlab.mixin.util.PropertyGroup(propList);
% matlab.mixin.CustomDisplay.displayPropertyGroups(o,propgrp);
end
end
end
end
Now if you build an array of your class, the display will look like:
>> foo(1) = MyVal(1); foo(2) = MyVal(2);
>> foo
foo =
1x2 MyVal members:
1: foo = 1
2: foo = 2
Of course this is just an example and is highly customizable. Unfortunately, I couldn't find a way to make that work for table objects.
Related
I have several properties that use essentially the same set method:
classdef MyClass
properties
A
B
end
methods
function mc = MyClass(a,b) % Constructor
mc.A = a;
mc.B = b;
end
function mc = set.A(mc, a) % setter for A
if a > 5
mc.A = a;
else
error('A should be larger than 5');
end
end
function mc = set.B(mc, b) %setter for B
if b > 5
mc.B = b;
else
error('B should be larger than 5');
end
end
end
end
Is there a way to use only one set function for variables A and B? (Please note that the error function use the property names as strings.)
Is it suggested to use only one set function? What are the possible drawbacks of using one set function?
The only real way is to extract the common code to another function, and call it from the setters:
classdef MyClass
properties
A
B
end%public properties
methods
function mc = MyClass(a,b) % Constructor
mc.A = a;
mc.B = b;
end
function mc = set.A(mc, value) % setter for A
mc = mc.commonSetter(value, 'A');
end
function mc = set.B(mc, value) %setter for B
mc = mc.commonSetter(value, 'B');
end
end%public methods
methods(protected = true)
function mc = commonSetter(mc, property, value)
if value <= 5;
error([property ' should be less than 5');
end
mc.(property) = value;
end%commonSetter()
end%protected methods
end%classdef
I have a class in MATLAB that represents an imaginary number. I have a constructor and two data members: real and imag. I am playing with overloading operator in a class and I want to make it work with matrices:
function obj = plus(o1, o2)
if (any(size(o1) ~= size(o2)))
error('dimensions must match');
end
[n,m] = size(o1);
obj(n,m) = mycomplex();
for i=1:n
for j=1:m
obj(i,j).real = o1(i,j).real + o2(i,j).real;
obj(i,j).imag = o1(i,j).imag + o2(i,j).imag;
end
end
end
But I don't want to use for loops. I want to do something like:
[obj.real] = [o1.real] + [o2.real]
But I don't understand why it does not work... the error says:
"Error in + Too many output arguments".
I know that in MATLAB it is good to avoid for loops for speed up... Can someone explain me why this does not work, and the right way to think about vectorization in MATLAB with an example for my function?
Thanks in advance.
EDIT: definition of my complex class:
classdef mycomplex < handle & matlab.mixin.CustomDisplay
properties (Access = public)
real;
imag;
end
methods (Access = public)
function this = mycomplex(varargin)
switch (nargin)
case 0
this.real = 0;
this.imag = 0;
case 1
this.real = varargin{1};
this.imag = 0;
case 2
this.real = varargin{1};
this.imag = varargin{2};
otherwise
error('Can''t have more than two arguments');
end
obj = this;
end
end
end
Consider the implementation below. First some notes:
the constructor can be called with no parameters. This is important to allow preallocating object arrays: obj(m,n) = MyComplex()
for convenience, the constructor accepts either scalar of array arguments. So we can call: c_scalar = MyComplex(1,1) or c_array = MyComplex(rand(3,1), rand(3,1))
the plus operator uses a for-loop for now (we will later change this).
(Note that I skipped some validations in the code, like checking that o1 and o2 are of the same size, similarly for a and b in the constructor).
classdef MyComplex < handle
properties
real
imag
end
methods
function obj = MyComplex(a,b)
% default values
if nargin < 2, b = 0; end
if nargin < 1, a = 0; end
% accepts scalar/array inputs
if isscalar(a) && isscalar(b)
obj.real = a;
obj.imag = b;
else
[m,n] = size(a);
obj(m,n) = MyComplex();
for i=1:m*n
obj(i).real = a(i);
obj(i).imag = b(i);
end
end
end
function obj = plus(o1, o2)
[m,n] = size(o1);
obj(m,n) = MyComplex(); % preallocate object array
for i=1:m*n % linear indexing
obj(i).real = o1(i).real + o2(i).real;
obj(i).imag = o1(i).imag + o2(i).imag;
end
end
end
end
An example of using the class:
% scalar objects
>> c1 = MyComplex(1,2);
>> c2 = MyComplex(3,4);
>> c3 = c1 + c2
c3 =
MyComplex with properties:
real: 4
imag: 6
% array of objects
>> c4 = [c1;c1] + [c2;c2]
c4 =
2x1 MyComplex array with properties:
real
imag
Now here is a vectorized version of the plus method:
function obj = plus(o1, o2)
[m,n] = size(o1);
obj(m,n) = MyComplex();
x = num2cell([o1.real] + [o2.real]);
[obj.real] = deal(x{:});
x = num2cell([o1.imag] + [o2.imag]);
[obj.imag] = deal(x{:});
end
I'm using the syntax: [objarray.propName] to reference a property in object arrays, this return the values as a vector.
For the opposite of assigning a property in an object array, I use comma-separated lists, thus I had to convert to a cell array to get the x{:} convenient syntax.
Note that the deal call is not strictly needed, we could write the assignment without it:
[obj.real] = x{:};
The line obj(n,m) = mycomplex() looks very suspicious. I think what you want to do there is obj = mycomplex(n,m) instead.
I can't see the rest of your code, but it's miraculous to me that this line even works. I suspect that you have an obj variable stored somewhere already, and this code simply overwrites one entry of that variable. I predict that if you clear all variables, it's going to fail on that line.
Again, it's very difficult to understand what happens without knowing what mycomplex() actually does.
I have a function which gives me two outputs, i need to use it in a for loop to create different variables, and i want to use them later in next loops. so i need to change their name during the for loop when they are created.
something like this:
for l=1:L
[A(l),B(l)] = function(l);
end
how can i do this so i could have A1,A2,... or B1,B2,....
thanks
If you do not wish to use cell arrays you can use structure with dynamic field names:
for l = 1:L
afn = sprintf('A%d', l ); % field name for first output
bfn = sprintf('B%d', l ); % field name for second output
[s.(afn) a.(bfn)] = func( l );
end
fieldnames( s ); % see all created variable names
Here is an example that you can modify to your needs.
function main // because we need to call `func`
L = 5; // max num of iterations
for l = 1:L
// replace `func` with the name of your function
eval(['[A' num2str(l) ', B' num2str(l) '] = func(' num2str(l) ')'])
end
end
// your function goes here
function [s, c] = func(x)
s = x*x;
c = s*x;
end
I have a function
function toto(a,b)
[out,~] = evalc(a)
% here I would like to call another function
myFunc(x,y,file);
end
How could I pass this function as args to toto function as sometimes I want to call toto(a,b) and some other times toto(a,b,#()myFunc(x,y) ?
(Answer before question edit: assumes fixed number of inputs to toto)
If you want to call an arbitrary function from within function toto: first define a handle to that function:
f = #myFunc;
and then pass that handle as input argument to toto, so that you can use it within toto:
function toto(a,b,f)
[out,~] = evalc(a)
f(x,y,file); %// call function whose handle is f
end
Define your function with an input to pass a function handle:
function toto(a,b,fun)
...
% You must know how many inputs and outputs to expect
% but nargin and nargout do work for function handles
% so you can handle different cases if needed.
[y1,y2,...] = fun(x1,x2,...);
...
Call the function and pass in a handle to the function:
toto(a,b,#FunName)
Or:
FunHandle = #FunName;
toto(a,b,FunHandle)
You can pass in additional parameters by using an anonymous function:
Param = 'FileName';
AnonFunHandle = #(x1,x2)FunName(x1,x2,Param);
toto(a,b,AnonFunHandle)
If you want to be able to use both the toto(a,b) and toto(a,b,f) or similar function calls, you need to use varargin and nargin (and their output counterparts). Here is a very basic example; it ignores any more than two outputs or any more than three inputs, and does not do any input checking etc.
function [vargout] = toto(a,b,varargin)
if nargin >2
func = vargin{1};
fout = func(a,b);
else
fout = [] % if no third argument is given returns empty
end
if nargout > 0
varargout{1} = a+b;
end
if nargout > 1
varargout{2} = fout;
end
end
So for example you can call this as x = toto(2,3) (returns x = 5), [x y] = toto(2,3) (returns x = 5, y = []), [x y] = toto(2,3,#(x,y)(x*y)) (returns x = 5, y = 6).
Suppose a is defined as the struct below. I tried to find equivalent command to R's dput but errs here. For example, I know that the below must be of the form struct('const',1,'terms',{{struct(),struct()}}) but I don't know what is stored inside the structure without going to check it with commands like here, time-consuming.
So by which command I can see the original command to generate a struct in Matlab?
>> a
a =
const: 1
terms: {[1x1 struct] [1x1 struct]}
Comment
Is it possible for you to switch from using a structure to a class? If so you could make one that mimics the struct, and each time it is modified call stack = dbstack to get the stack - then store the stack along with the change. The command that made the change could then be retrieved automatically later from the line numbers in the stack.
As a follow-up request to this in the comment, here is an example of a class that provides struct functionality and also keeps a record of its assignments:
classdef utstruct
properties (SetAccess = private)
modifications
end
properties (Dependent, SetAccess = private)
myStruct
end
properties (Access = private)
m_struct
end
methods
function self = utstruct(varargin)
if nargin > 0
self.m_struct = builtin('struct', varargin{:});
else
self.m_struct = builtin('struct');
end
% Should update self.modifications here
end
function B = subsref(self, s)
if any(strcmp(s(1).subs, properties(self)))
B = builtin('subsref', self, s);
else
B = subsref(self.m_struct, s);
end
end
function A = subsasgn(self, s, b)
self.m_struct = subsasgn(self.m_struct, s, b);
newMod = builtin('struct');
newMod.type = 'subsasgn';
newMod.modData = {s b};
newMod.stack = dbstack;
self.modifications = [self.modifications; newMod];
A = self;
end
function disp(self)
disp(self.m_struct);
end
function names = fieldnames(self, varargin)
names = fieldnames(self.m_struct, varargin{:});
end
function C = cat(self, dim, varargin)
uts = cellfun(#(x)isa(x, 'utstruct'), varargin);
varargin{uts} = cellfun(#(x)x.m_struct, varargin(uts));
varargin = [{self.m_struct} varargin];
self.m_struct = cat(dim, varargin{:});
% Should update self.modifications here
C = self;
end
function C = horzcat(self, varargin)
C = self.cat(1, varargin{:});
end
function C = vertcat(self, varargin)
C = self.cat(2, varargin{:});
end
function value = get.myStruct(self)
value = self.m_struct;
end
end
end
You should add some code to update the modifications array when initialisation / concatenation operations occur.
The subsref and subsasgn overrides are the key points here that make it behave like a struct (by deferring all their activity to an actual struct), but other overrides like fieldnames and disp do the same thing. In subsasgn a record of all the assignments to the struct is kept, along with the stack that generated the assignment.
Note: for this to be fully compatible with the built-in struct you probably should override a few more methods, but this should be enough to get you started. See Subclassing MATLAB Built-In Types.
Edit: I made the example a bit more robust. It's now a value class - as it should be - and works with concatenation.
Edit: You can avoid using a find-and-replace to refactor existing struct(...) calls by redefining the function struct:
function s = struct(varargin)
% STRUCT Overrides default struct function to provide unit-testable structs
%
% Set global variable unitTestStructEnabled to true to enable this
% function.
%
global unitTestStructEnabled;
if isempty(unitTestStructEnabled)
unitTestStructEnabled = false;
end
if unitTestStructEnabled
s = utstruct(varargin{:});
else
s = builtin('struct', varargin{:});
end
You probably don't want that hanging around on your path the whole time, as you will get a warning when you first create a struct (you could turn it off, but that might hide other problems), so you should probably put it in a folder that's not normally in the path, and temporarily add it to the path for unit testing (addpath / rmpath).
Here's the skeleton of a dumpvar function, i.e. something along the idea of tashuhka
function str = dumpvar(a)
switch class(a)
case 'double'
if isempty(a)
str = '[]'; % bug when "a" is multidimensional and empty
elseif isscalar(a)
str = num2str(a);
elseif isrow(a)
str = strcat('[', dumpvar(a(1)));
for k = 2:size(a,2)
str = strcat(str,',',dumpvar(a(k)));
end;
str = strcat(str, ']');
elseif numel(size(a)) == 2
str = strcat('[', dumpvar(a(1,:)));
for k = 2:size(a,1)
str = strcat(str,';',dumpvar(a(k,:)));
end;
str = strcat(str, ']');
else
do_what_i_mean();
end;
case 'struct'
fn = fieldnames(a);
if isempty(fn)
str = 'struct()';
elseif isscalar(a)
str = strcat('struct(''', fn{1},''',', dumpvar(a.(fn{1})));
for k=2:numel(fn)
str = strcat(str,',''',fn{k},''',', dumpvar(a.(fn{k})));
end;
str = strcat(str, ')');
else
do_what_i_mean();
end;
otherwise
do_what_i_mean();
end;
function do_what_i_mean()
throwAsCaller(MException(...
'beingLazy:onSaturday:Fault', ...
'Storage of class "%s" and arity %d is not implemented yet. Would you?', ...
class(a), numel(size(a))...
));
end;
end
Save it in a dumpvar.m file somewhere in Matlab's path, then test it with this code snippet:
a = struct(...
'field1', 1,...
'field2', [],...
'field10', struct(...
'field3', [1 2;2 3;3 4],...
'field4', struct()...
)...
);
disp(dumpvar(a));
eval(sprintf('b=%s;', dumpvar(a)));
Please note that this function is still in a toy stage: it's anywhere near exhaustive (lacks treatment of struct arrays, cells, char, logical and other fundamental types, not to mention user-defined classes --- hehe, those would be a pickle) and it is meant to be completed by you with whatever functionality you need.
Although it does not answer completely your question, vsize() by Urs can help you:
% Create some complicated variable
v(1).a{1}=sparse(magic(3)+2i*magic(3));
v(2).a{2}={struct('FA',{'a','bb'},'FB',{magic(5),{}})};
v(2).b{2}=#(x) sind(x);
% Dissect
P = vsize(v);
% -------------------------
% 1998 1998 B * v = 2:1x2:struct.(2)
% CELL ----- 360 B v[].a = 2:1x1:cell
% 1750 248 B - v[].a{} = 2:3x3:double.sparse.complex
% CELL ----- 1014 B v[].a = 2:1x2:cell
% 1750 0 B - v[].a{} = 2:0x0:double
% CELL ----- 894 B v[].a{} = 2:1x1:cell
% STRUCT --- 782 B v[].a{}{} = 2:1x2:struct.(2)
% 1748 2 B - v[].a{}{}[].FA = 2:1x1:char
% 1744 4 B - v[].a{}{}[].FA = 2:1x2:char
% 1544 200 B - v[].a{}{}[].FB = 2:5x5:double
% CELL ----- 0 B v[].a{}{}[].FB = 2:0x0:cell
% 1544 0 B - v[].b = 2:0x0:double
% CELL ----- 152 B v[].b = 2:1x2:cell
% 1544 0 B - v[].b{} = 2:0x0:double
% 1512 32 B - v[].b{} = 2:1x1:function_handle