How to assign to a collection of object properties stored in an object array, using a numerical array of compatible type and size (one statement, without for-loop!)?

I am building a minimal discrete-event simulation support, where sequences of events will be initialized from external weather records (of precipitation, for example). Class EventList_MWE (subscript '_MWE' denoting 'minimum working example') implements the DEVS list using a circular array (buffer) of events of base class MySimEvent, from which I derive user-specific event classes and methods, such as DerivedSimEvent. To handle polymorphic events, MySimEvent inherits from the class matlab.mixin.Heterogeneous.
The event list class is an aggregation of an object array of MySimEvent objects (objArray) and has further properties, but is not inherited from an array class. The constructor EventListExp(N) initializes the buffer with N events with undefined (NaN) properties:
objArray(1:N) = MySimEvent().
Events are currently Value, not Handle, class objects; and event properties are kept public to be accessible from EventListExp.
The error occurs in my method EventListExp.ArrayInit(obj, T, ID), which initializes a subset of events using as input the arrays T and ID:
[obj.objArray(IndexSet).tstmp] = T(1:nI);
Error: Insufficient number of outputs from right hand side of equal sign to satisfy assignment.
IndexSet is an array of valid indices of the same size as T(1:nI) (in my example: 10); both arrays are double-typed.
I also tried subsasgn function and defined a “substruct” structure argument, but I do not yet overload / override the function:
subs = substruct('.','objArray', '()',{IndexSet}, '.','tstmp');
obj = subsasgn(obj, subs, T(1:nI));
Error: Expected one output from a curly brace or dot indexing expression, but there were 10 results.
Questions: Isn't the property collection (array) enclosed in square brackets '[]' a valid target for an assignment (i.e. a vallid left-hand-side)? Do I wrongly use subsasgn for indexing, or must I overwrite subsasgn (in which class?)? Or is my approach pointless, since no performance will ever be gained compared to iterating event constructor calls?
rng('shuffle');
N = 25;
DEVS_List_Test = EventList_MWE(N);
for i=1:10
DEVS_List_Test.InsertEvent(randi(10,1), i, 1, rand());
end
T = rand(1,N);
ID = randi(100,1,N);
DEVS_List_Test.ArrayInit(T, ID);
Insufficient number of outputs from right hand side of equal sign to satisfy assignment.

Error in EventList_MWE/ArrayInit (line 27)
[obj.objArray(IndexSet).tstmp] = T(1:nI);

 Accepted Answer

@Peter, without getting into your specific code I suspect that [obj.objArray(IndexSet).tstmp] = T(1:nI) is incorrect in that the tstmp should be indexed not the objArray: obj.objArray.tstmp(IndexSet) = T(1:nI) (note that the brackets are only needed on the assignment side when expecting individual outputs). However after getting it to run with minor changes to your test program this is what it should be changed to use deal since you are expecting multiple outputs:
% Fix in EventList_MWE
[obj.objArray(IndexSet).tstmp] = deal(T(1:nI));
[obj.objArray(IndexSet).evid] = deal(ID(1:nI));
[obj.objArray(IndexSet).evtype] = deal(ones(1,nI));
% Fix in TestSimEvent_MWE
T = rand(1,DEVS_List_Test.N);
ID = randi(100,1,DEVS_List_Test.N);
Also, a subsasgn override in a base or derrived class is only needed if you are trying to change the default behaviour of indexing on the assignment size. See exampe in my Utility package: Utility package containing NoGrow for fixed size arrays - File Exchange - MATLAB Central (mathworks.com)

6 Comments

@Jeffrey Clark: Thanks for your quick response! I still can't quite get it to work, though:
  1. obj.objArray.tstmp(IndexSet), as you suggested, is an invalid expression and gives this error: Expected one output from a curly brace or dot indexing expression, but there were 25 results.(objArray has 25 elements/objects, indeed, and tstmp is a property of each object). This behavior appears reasonable, since objArray is an array of objects and can be indexed and, of the selected objects, each one has a property ‘tstmp’. To change this default indexing semantics, I might have to override / overload something (subsasgn).
  2. Expression obj.objArray(IndexSet).tstmp (without the brackets [] around) cannot be assigned to: it yields all property (tstmp) values individually ‘packed’ and returns as size(.) this: 1 1 1 1 1 1 1 1 1 1 (i.e. one, 10-times repeated), whereas [obj.objArray(IndexSet).tstmp] has size (1,10) and type double, as desired.
  3. [obj.objArray(IndexSet).tstmp] = deal(T(1:nI)) throws no error (whatever the 'deal' function deals with, it is new to me), but now it assigns to each object selected (out of 10) the whole array T(1:nI), i.e. 10 time stamps, rather than assigning T(i) to object nr. i in turn! Applying deal(.), the result size([obj.objArray(IndexSet).tstmp]) now becomes (1, 100).
@Peter, yes I jumped the gun to see if the deal result would be what you wanted (I was satisfied by not getting an error in whatever you were doing with it). In my years of MATLAB use I have tried to prevent the situation you have with trying to store into a comma separated list. It would not be a problem if you had a corresponding sized comma separated list on the right side. I've tried again to see if MATLAB had something builtin for this but haven't found anything. You could create a function for this and use that function call where you needed it, or you can implement these statements wherever you need to do ti:
temp = mat2cell(T(1:nI),ones(size(T(1:nI),1),1),ones(1,size(T(1:nI),2)));
[obj.objArray(IndexSet).tstmp] = temp{:};
% or just
[obj.objArray(IndexSet).tstmp] = mat2csl(T(1:nI));
[obj.objArray(IndexSet).evid] = mat2csl(ID(1:nI));
[obj.objArray(IndexSet).evtype] = mat2csl(ones(1,nI));
% if you define this function
function varargout = mat2csl(A)
varargout = mat2cell(A,ones(size(A,1),1),ones(1,size(A,2)));
end
@Jeffrey Clark, thank you once again for your help! Your solution using mat2csv / mat2cell now works fine for me, i.e., seems to produce correct results and no error. Still, the underlying problems with Matlab (My understanding of the language? The interpreter?) remain somewhat enigmatic to me.
  1. Matlab suggests replacing ‘mat2cell’ with ‘num2cell’ for simplicity and speed (warning), but this didn’t work! After all, the conversion of double [] to cell (and, even more so, to csv which I conceive as ASCII (char/string) is a bit heavy. I am wondering if this array solution will be less or more efficient than scalar object methods called in a loop?!?
  2. What precisely ‘is’ (type/class, size?) the result mat2cell(A, ones(size(A,1),1), ones(1, size(A,2))) for a double array A=T(1:nI)? The command line interpreter, and disp(.) alike, show just one double value, but it really is, correctly, cell{1:10} of double! Why is a cell array needed to assign it, and is it still a vector assignment? What does Matlab’s operational semantics specification say about it? (There are several issues combined, index expressions (), ‘.’-reference to class properties, object arrays / vector assignment, etc.) Or, put more practically, can the interpreter and its error messages be ‘deeply debugged’ to see which results it produces and when and why it fails?
@Peter, MATLAB has a long history that served the engineering community to test application of applied mathematics for developing specifications for software programmers to follow. About the same time Mathematica was mostly used by the scientific community because it was based upon symbolic math solution and generation of scientific papers that enabled engineers to design their subsystems. Somewhere along their developments MATLAB added more scientific support via symbolic processing and also more software prototyping support with constructs closer to what programmers used with more modern languages. Unfortunately this lead to some inconsistent legacy and newer constructs and gaps that are sometimes frustrating. As an example, I try to do as much matrix processing as I can because it is more compact than loops but don't know if this will always be faster to develop or execute until I hit some sort of brick wall.
Cell, struct and the latest table, are some that I don't try to use much. They are containers that allow use of multiple basic types (double, integer, logical...) and/or user defined types (e.g., class) within one variable. A similar thing could be accomplished by making your own class (which by the way wasn't originally in MATLAB and has changed again since its inclusion) to do this but using the provided containers come with some direct MATLAB functional support. But sometimes the support is incomplete because, as in any project, increases in scope after development begins leads to unforseen (e.g., not strict systems-engineering-development waterfall) consequences.
To really answer one of your questions, you can replace mat2cell(A,ones(size(A,1),1),ones(1,size(A,2))) with just num2cell(A), e.g., just remove the extra arguments in mat2cell. The only reason I convert to a cell is that is the only mechanism I could find that allows conversion to comma separated list.
@Jeffrey Clark: Again, many thanks for your detailed and competent explanations on Matlab, its history and attempts to serve different user communities! Accidentally, I found the Mathworks web site design and help desk software very workable and successful.
Regarding your last comment, I did try replacing mat2cell with num2cell - as Matlab suggests for speeding up - but that missed the hole (with the num2cell arguments I provided). To improve on runtime, I'll probably need to try other options; e.g. using handle event classes, instead of Value, and utilize class inheritance but for the truly user dependent parts (event handling).
@Peter, with the test code you provided (except changing to rng(0) to compare runs) this code seems to give the same results whether mat2cell or num2cell is used. I've made this more robust and am going to put it in my utilites for future needs; I don't know why/what problem you are seeing with num2cell:
function varargout = mat2csl(varargin)
if nargin==nargout
varargout = varargin;
elseif nargin~=1
throwAsCaller(MException('MATLAB:maxrhs','Too many input arguments.'))
elseif numel(varargin{1})==1
[varargout(1:nargout)] = deal(varargin);
elseif numel(varargin{1})==nargout
varargout = num2cell(varargin{1});
% varargout = mat2cell(varargin{1},ones(size(varargin{1},1),1),ones(1,size(varargin{1},2)));
elseif numel(varargin{1})<nargout
throwAsCaller(MException('MATLAB:maxlhs','Too many output arguments.'))
else
throwAsCaller(MException('MATLAB:minlhs','Too few output arguments.'))
end
end

Sign in to comment.

More Answers (0)

Categories

Products

Release

R2020a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!