8 views (last 30 days)
Daniel Shub on 4 May 2011
How do you support colon notation in an overloaded subsref? I am trying to overload subsref for a class I am writing. I have an object obj with property prop. I want obj.prop(n, m) to call a method obj.calcprop(n, m). For simplicity, assume calcprop returns obj.x(n, m) (where x is another property). I cannot get calcprop to return the correct size output with colon notation: obj.prop(:). My subsref knows what the maximum values of n and m, nMax and mMax and I can use this to handle colon notation when nMax is greater than 1. For example,
nMax = 2; mMax = 10;
x = rand(nMax, mMax);
isequal(size(x(:)), size(x((1:(nMax*mMax))')))
If I call obj.calcprop((1:(nMax*mMax))', []) and handle the empty argument in a special way. The problem is that nMax = 1; mMax = 10; x = rand(nMax, mMax); isequal(size(x(:)), size(x((1:(nMax*mMax))'))) doesn't work. These also do not work:
isequal(size(x(:)), size(x(1:(nMax*mMax))))
isequal(size(x(:)), size(x(1, 1:mMax)))
isequal(size(x(:)), size(x(1, (1:mMax)')))
Do I just take the "wrong" size output of calcprop and deal with it in my overloaded subsref?

Malcolm Lidierth on 28 Sep 2011
Daniel Is this any use? It is called from subsref in some of my own custom objects and converts from substruct to subs:
function [subs, index]=struct2sub(siz, index)
% struct2sub returns the subscripts from a substruct structure
% Examples:
% subs=struct2sub(siz, index);
% [subs, index]=struct2sub(siz, index);
%
% For a matrix M, struct2sub returns the subscripts
% subs=struct2sub(siz, index);
% where siz=size(M) and index is a substruct object (as used by
% subsref or subsasgn methods)
% M may be a field of a structure or object referenced through
% index
%
% The output subs is a cell array of subscripts suitable for use
% with the standard MATLAB indexing and sub-referencing functions.
%
% If required, the output index is a copy of the input index
% with the subs field of the last element fully expanded
% numerically (i.e. all ':' are replaced with the appropriate
% vectors)
%
% Generate linear indices from the result using the standard MATLAB sub2ind
% function:
% subs=struct2sub(siz, index);
% ind=sub2ind(siz, subs{:});
n=numel(index(end).subs);
for k=1:n
% Deal with non-numeric subscripts
if ischar(index(end).subs{k})
% ':' is the only possibility
index(end).subs{k}=1:siz(k);
end
end
subs=cell(1,n);
if all(cellfun(@numel, index(end).subs)==1)
% All scalar entries
[subs{:}]=index(end).subs{:};
else
subs=cell(1,numel(index(end).subs));
[subs{:}]=ndgrid(index(end).subs{:});
for k=1:numel(subs)
subs{k}=subs{k}(:).';
end
end
return
end

Ulrik on 20 Oct 2011
Thanks for the code... I rewrote it bit. e.g did not work with index (true/false), an empty s.subs e.g. you can do this in matlab:
a = [];
a()
Info: Please note that cell's are not supported "{}" in my version
-------------------------------------
function [sub,s] = struct2sub(siz, s)
% Initial check of input s
if numel(s) == 2 && strcmp(s(1).type,'.') && ischar(s(1).subs) && strcmp(s(2).type,'()'),
s(1) = [];
elseif ~(numel(s) == 1 && strcmp(s(1).type,'()')),
error('struct2sub:InvalidStructure','Input error: Structure of s is not supported');
end;
% Preforms ajustment of the input variable siz
if length(siz) ~= numel(s.subs),
if ~numel(s.subs),
s.subs = cell(size(siz));
s.subs(:) = {':'};
elseif length(siz) < numel(s.subs)
siz = [siz ones(1,numel(s.subs)-length(siz))];
else
%Adjust for linear indexing on last element
siz = [siz(1:numel(s.subs)-1) prod(siz(numel(s.subs):end))];
end
end
for sCount = 1:numel(s.subs)
if ischar(s.subs{sCount}) && strcmp(strtrim(s.subs{sCount}),':'),
s(end).subs{sCount} = 1:siz(sCount);
elseif islogical(s.subs{sCount})
if numel(s.subs{sCount}) ~= siz(iCount),
error('struct2sub:InvalidStructureSubsSize','Input error: s.subs of wrong size');
end;
s(end).subs{sCount} = find(s(end).subs{sCount});
end;
end;
[sub{1:numel(s.subs)}] = ndgrid(s.subs{:},1);
for sCount = 1:numel(sub),
sub{sCount} = sub{sCount}(:)';
end;

1 Comment

Malcolm Lidierth on 23 Oct 2011
@Ulrik
Glad it was of some use. Note that calling ndgrid is not a good general solution. It can be slow and the output will generally take more memory than the data you are trying to access.
struct2sub was designed to be called from a subsref method - not to replace it. In my code it is only called when more efficient methods are not available and the subsref takes care of e.g. logical indices, '()' etc, missing dims etc.