'Subscripted assignment between dissimilar structures' thrown on empty struct

404 vues (au cours des 30 derniers jours)
As shown in the code below, I'm attempting to populate a struct array iteratively. I have a function which returns a 1x1 struct - don't worry all of the fields returned are identical across function calls - and I wish to store these output as entries in a struct array.
The problem I am running into occurs when I attempt to assign the first returned 1x1 struct to the first entry in my struct array. Below is a working dummy example of what happens:
struct_array = struct([]);
first_struct = struct('field1',1,'field2',2);
% This throws an error:
struct_array(1) = first_struct;
Removing the index from struct_array in the last line fixes the error. This makes sense since it's overwriting (not modifying) the struct defined previously. The work around I plan to use for now is as follows:
struct_array = struct([]);
for i =1:length(input)
next_struct = struct_fn(input{i})
if isempty(struct_array)
struct_array = next_struct;
else
struct_array(i) = next_struct;
However this just seems like a silly way to go about it. Is there a better way?
But further, why is this a feature?
I can understand the justification for throwing the error on indexed assignment to a 1x1 struct with no fields (the output of struct()) since the fact that it has non-zero dimensions would seem to imply that the container has been instantiated, with fields given by the empty set. So the error is thrown based on a comparison of the fields of the attempted assignment and the empty set.
However, when the struct array in question is initialized with struct([]) it is a 0x0 struct array with no fields, and so it would seem to imply that the container hasn't been instantiated and the fields are given by null. So indexed assignment should instantiate the struct and its fields.
Clearly though this is not how it works since fieldnames(struct()) and fieldnames(struct([])) both return 0x1 empty cell arrays. In fact, the only discernible difference I can find between struct() and struct([]) is that when given as input to the 'isempty' function they return (sensibly enough) true and false, respectively.
So my final question is, other than that, what's the difference between struct() and struct([])?

Réponse acceptée

Matt J
Matt J le 3 Mar 2013
Modifié(e) : Matt J le 3 Mar 2013
Like any MATLAB array, it is bad to iteratively concatenate to a struct array because it forces lots of unnecessary memory reallocation. It would be better if you pre-allocate the array by doing
N=length(input);
struct_array(N)=struct('field1",[],'field2',[]);
for i=1:N
struct_array(i)=struct_fn(input(i));
end
Alternatively, instead of pre-allocating before the loop, you could also loop backwards so that all memory allocation is done in the first iteration of the loop.
for i=N:-1:1
struct_array(i)=struct_fn(input(i));
end
  5 commentaires
Matt J
Matt J le 4 Mar 2013
Modifié(e) : Matt J le 5 Mar 2013
you will only receive this upon access, not assignment. For example, the following works:
OK, well that has clued me in to what is going on. The reason that indexed assignment
struct_array(1) = first_struct;
gives the error you see is because the indexing tells MATLAB that you are assigning into an existing array. Assignment into an existing array will always result in MATLAB attempting to convert the right hand side to the type on left hand side, so that all the array elements can meaningfully co-exist within the same array object. Because first_struct has a different set of fields from struct_array, this conversion is undefined.
This is a general rule across all MATLAB data types. For example,
a=[]; a(1)=1;
works, as you noted, but the following does not, again because the required conversion is ambiguous.
>> a=[]; a(1)={1}
The following error occurred converting from cell to double:
Error using double
Conversion to double from cell is not possible.
In any case, I wonder what the purpose of your original question is if you aren't able to pre-allocate. What advantage were you looking for in defining an initial empty struct at all if it's just going to get overwritten by an iteratively growing struct array?
Matt B
Matt B le 5 Mar 2013
That's spot on, thanks! As you pointed out, defining the initial empty struct is completely unnecessary, and in fact the reason why the error occurs. I think I assumed (carelessly) that I needed to associate the variable name with a struct object before I tried to assign a struct to it.
In any case, thank you!

Connectez-vous pour commenter.

Plus de réponses (3)

Martin Juhl
Martin Juhl le 9 Jan 2014
One easy hack that works:
% cell array of structs
list = cell(N,1)
for i = 1:N
list{i} = struct('field1',1,'field2',2);
end
% array of structs
list = [list{:}];
  1 commentaire
Matt J
Matt J le 9 Jan 2014
Modifié(e) : Matt J le 9 Jan 2014
It works, but it can be costly to call the struct() constructor repeatedly in a loop. Compare,
N=1000;
tic;
list = cell(N,1);
for i = 1:N
list{i} = struct('field1',1,'field2',2);
end
list = [list{:}];
toc
%Elapsed time is 0.011689 seconds.
tic;
list(1:N)=struct('field1',1,'field2',2);
toc
%Elapsed time is 0.000243 seconds.
In the OP's case, I guess it may not matter if the loop contains other heavy functions calls...

Connectez-vous pour commenter.


Jim Hokanson
Jim Hokanson le 10 Août 2013
If you choose not to initialize you can loop backwards however it is generally best to declare the variable as a structure. This should not be done with:
struct([])
but rather with:
struct()
The entire code becomes:
struct_array = struct();
for i=N:-1:1
struct_array(i)=struct_fn(input(i));
end
or if N will ever be 0
if N == 0 struct_array = struct([]) else %Insert code from above end
Doing this ensures that struct_array is in fact a structure and that you don't retain any previous values if this variable was previously used as a structure array. Consider the following:
s = 3;
%------ Go down a bunch of lines later
for i = N:-1:1
s(i) = struct_fn(input(i)); %Oops, this will throw an error
end
or even worse
s(N+30) = some_value;
%---------- Go down a bunch of lines later
for i = N:-1:1
s(i) = struct_fn(input(i)); %Now s has extra elements.
end
99% of the time no initialization is needed, but I think it is better to add struct() prior to the loop

David Young
David Young le 21 Jan 2014
For some discussion of the different kinds of empty struct arrays, and also for an easy way to initialize struct arrays that are empty in different ways, you could have a look at my File Exchange submission emptyStruct.

Catégories

En savoir plus sur Structures dans Help Center et File Exchange

Produits

Community Treasure Hunt

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

Start Hunting!

Translated by