1

I have a cell with mathematical expressions that I would like to convert to a numeric array. It look as follows:

a = {};
a{1,1} = '0.55';
a{2,1} = '0.25 + 0.50';

Now I would like to receive the result (but preferably without a for loop):

b(1) = 0.55;
b(2) = 0.75;

How can I achieve this efficiently?

WJA
  • 6,676
  • 16
  • 85
  • 152
  • 4
    "efficient" would be to restructure your code to not rely on string-evaluation at all, because that involves `eval`. On why `eval` is almost a bad idea, see [this answer of mine](https://stackoverflow.com/a/32467170/5211833). This is very reminiscent of an [XY problem](https://meta.stackexchange.com/q/66377/325771), so please, ask about the problem, not your proposed solution if you want a meaningful answer on efficiency, as opposed to a very slow and crude stitch. – Adriaan Nov 21 '18 at 09:24
  • 1
    As a simple way to notice why `eval` can be bad, assume `a{1,1}='exit'`. For a less damaging example, assume it has a mathematical expression that results in an infinite loop,or something of the likes. – Ander Biguri Nov 21 '18 at 09:43
  • What about `str = [ '[' , sprintf('%s ',a{:}) '];'];b = eval(str);` ? – rahnema1 Nov 21 '18 at 10:37

1 Answers1

2

b = cellfun(@eval,a); will create an array b of the same size as cell array a, with each value the evaluation of the corresponding string in the cell array.

a = {};
a{1,1} = '0.55';
a{2,1} = '0.25 + 0.50';

a=repmat(a,1000,20); %Make it big for performance evaluation

tic
b1 = cellfun(@eval,a);
toc %0.662187 seconds

Another option is to make a big string expression so that eval is called only once rather than several times due to cellfun internal loop. This is less safe as abnormal values in the cell array a will likely cause the code to crash, while it may simply produce NaN in the code above.

tic
% add a comma separator after each value
strCell = cellfun(@(x) [x ','],transpose(a),'uniformoutput',false);
% add a semicolon separator at the end of each row
strCell(end,:) = cellfun(@(x) [x(1:end-1) ';'], strCell(end,:), 'uniformoutput',false);
% remove the last separator
strCell{end}=strCell{end}(1,end-1);
% evaluate the line
b2=eval(['[' strCell{:} ']']);
toc %0.313738 seconds but sometimes more than 1 seconds
Brice
  • 1,560
  • 5
  • 10
  • 1
    Using eval is not so efficient. See https://nl.mathworks.com/help/matlab/matlab_prog/string-evaluation.html. Are there better ways/quicker? – WJA Nov 21 '18 at 09:14
  • There are not that many string evaluation functions. `str2num` internally relies on `eval` and will not be more efficient (`edit str2num` to see the actual code). `str2double` does not work on expressions – Brice Nov 21 '18 at 09:18
  • 1
    You'll want to put a large warning here that `eval` is nearly always harmful for speed in your code. I agree that it's the sole option here, but one cannot warn people enough to try and avoid having to use `eval` whenever possible, see e.g. [this answer of mine](https://stackoverflow.com/a/32467170/5211833) and links therein on why this is the case. – Adriaan Nov 21 '18 at 09:25
  • At least I learned something new here; `cellfun` does support `eval`. Not that that makes it any better for use of course. Also note that `cellfun` is just a MATLAB wrapper around a loop, not even a FORTRAN loop. So whilst technically you're not writing the loop yourself, MATLAB still uses a slow, high-level loop (as opposed to vectorisation, which employs low-level FORTRAN loops). – Adriaan Nov 21 '18 at 09:33
  • I added a second version where `eval` is called only once which is slightly better in terms of performance. It still relies on that damn function – Brice Nov 21 '18 at 09:42
  • 2
    @Adriaan `cellfun` is nothing more than a high level loop, except for those functions listed in the 'Backward compatibility' paragraph of the documentation: *isempty, islogical, isreal, length, ndims, prodofsize, size, isclass*. If these are passed as a string rather than a function handle, the performance of the function is greatly enhanced (which indicates some low-level loop is being used). Like, `cellfun('isempty',C)` is way faster than `cellfun(@isempty,C)` – Brice Nov 21 '18 at 09:47