Question

I'm trying to create a structure array at runtime in Matlab.

    A= {'dark';'oa_45'; 'oa_225'};

    for i = 1:3
      tmp =load([folder '/' A{i} '.txt']);
      eval([A{i} '.count=tmp(:,1:2)']);
      eval([A{i} '.mean=mean(tmp(:,1:2),1)']);
      eval([A{i} '.sqrtmean=sqrt(' A{i} '.mean)']);
      eval([A{i} '.stdev=std(tmp(:,1:2),1)']);
      eval(A{i});
    end

Since I know, that using eval is a rather bad practice, I would like to know whether there is a simple way to avoid eval here.

I figured it would be possible to create the structure array before the loop and then assign only the sub fields in the loop with the parenthesis notation:

   s.(A{i}).count = ...

I found some suggestions here that say, it seems to be possible with subsasgn. That seemed rather more complicated than the eval function.

Does somebody know a simple way to avoid the eval function, or is it just the best call here? I'm just asking out of curiosity, I guess for these three vectors, the loss in performance doesn't really matter.

Best Regards, Mechanix

Was it helpful?

Solution

Assuming you would be okay with having a single struct that will hold the three structs named 'dark','oa_45', 'oa_225' and the field 'sqrtmean' being dropped down to the bottom, you might wanna try this -

Code Version 1 (Recommended)

A= {'dark';'oa_45'; 'oa_225'};
fieldnames1 = {'count';'mean';'stdev'};
funcnames1 = {'';'mean';'std'};

for k = 1:numel(A)
    tmp =load([folder '/' A{k} '.txt']);
    struct1 = A{k};
    for i = 1:numel(fieldnames1)
        if isempty(funcnames1{i})
            comp_struct.(struct1).count=tmp(:,1:2);
        else
            fh = str2func(funcnames1{i});
            comp_struct.(struct1).(fieldnames1{i}) = fh(tmp(:,1:2),1);
        end
    end
    comp_struct.(struct1).sqrtmean = sqrt(comp_struct.(struct1).mean);
end

Thus, 'comp_struct.dark' would be your original 'dark' and so on.

Go one step further and remove the IF-ELSE -

Code Version 2 (Highly Recommended)

A= {'dark';'oa_45'; 'oa_225'};
fieldnames1 = {'count';'mean';'stdev'};
funcnames1 = {'donothing';'mean';'std'};

for k = 1:numel(A)
    tmp =load([folder '/' A{k} '.txt']);
    struct1 = A{k};
    for i = 1:numel(fieldnames1)
        fh = str2func(funcnames1{i});
        comp_struct.(struct1).(fieldnames1{i}) = fh(tmp(:,1:2),1);
    end
    comp_struct.(struct1).sqrtmean = sqrt(comp_struct.(struct1).mean);
end

Don't forget to add this function in path -

function out = donothing(varargin)
out = varargin{1};

If the nested loops are bothering you or you don't want to get into function handles and you were only looking to replace EVAL with something that would occupy the same space in terms of code lines, use the the following, but I won't recommend for a general case though -

Code Version 3 (Not Recommended)

A= {'dark';'oa_45'; 'oa_225'};
for k = 1:numel(A)
    tmp =load([folder '/' A{k} '.txt']);
    comp_struct.(A{k}).count = tmp(:,1:2);
    comp_struct.(A{k}).mean = mean(tmp(:,1:2),1);
    comp_struct.(A{k}).sqrtmean = sqrt(comp_struct.(A{k}).mean);
    comp_struct.(A{k}).stdev = std(tmp(:,1:2),1);
end

OTHER TIPS

But you're not creating "a structure array", you're creating 3 separate struct variables! An actual structure array would be highly preferable:

A = {'dark';'oa_45'; 'oa_225'};

% Preallocation is obviously optional, but good practice
% A cell array initialiser will make structarray the same size
structarray = struct('name',A,'count',[],'mean',[],'sqrtmean',[],'stdev',[]);

for i = 1:length(A)
  tmp = load([folder '/' A{i} '.txt']);
  % structarray(i).name = A{i};   % if we didn't preallocate
  structarray(i).count = tmp(:,1:2);
  structarray(i).mean = mean(tmp(:,1:2),1);
  structarray(i).sqrtmean = sqrt(structarray(i).mean);
  structarray(i).stdev = std(tmp(:,1:2),1);
end

Avoiding eval can often involve a fair bit of restructuring, but in almost all cases the code you end up with is more robust and easier to work with.

For example, relying on specifically named variables is fine when you start out with the command line and simple scripts in the base workspace - it makes sense for the user, to keep track of what's what. Once you progress to bigger problems you'll find it really doesn't scale well and you need to do things in a way that makes sense for the program - when you're passing data between functions the names become irrelevant anyway. That's when tools like cell arrays and struct arrays really come into their own. Structs are particularly handy since you can nearly always put in an extra field purely to identify things, as I have with name above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top