Particularly strange bug using the eval function
Show older comments
In the MWE below, I define a bunch of parameters using an eval command. I know that the experts recommend against using eval, as in this thread, but I don't really see an alternative in this instance. I also know that MATHWORKS would prefer me not to define variables that clash with their own builtins. However, the code still should work properly, but doesn't in this example.
The class for rho is correctly defined as double. But the fourth last line returns an error, as does the second last line. In both cases the code still thinks gamma is matlab's built-in function. Yet the third last line indicates that gamma is correctly defined as 0.6. Note that if I remove the first line of the code, and thus run the code in the base workspace, everything works as expected.
Could somebody please explain a) what's causing this bug and b) a simple alternative to my use of the eval function?
function nothing
rhoY = 0.8;
phiY = 0.8;
gammaY = 0.6;
etaY = 0.05;
muY = .5;
ecLetter = 'Y';
Params = {'phi','rho','eta','gamma','mu'};
nParam = numel(Params);
for ii=1:nParam;
thisParam = Params{ii};
eval([thisParam '=' thisParam ecLetter ';']);
end;
%This kludge fixes the problem
tmp=gamma;
gamma=tmp;
class(rho)
class(gamma)
gamma
3*gamma
keyboard;
Accepted Answer
More Answers (4)
Walter Roberson
on 2 Aug 2020
4 votes
This is defined behavior for the execution engine for the last few releases, and is not a bug. R2018b is the release that is springing to mind as when the change was made, but that would have to be re-checked.
The behavior defined is this:
Inside a function (and you are inside a function), when you have a reference to a name that MATLAB can see as a function on the search path, and there is no "plain text" assignment to a variable of that name, then MATLAB will now assume that plain-texts references to the name always refers to the function, even if the name is being assigned to by some side effect such as eval() or evalin() or executing a script.
A way of thinking about this is that MATLAB is now doing static analysis at the time the file is parsed, ignoring any potential non-obvious assignment.
The work arounds include:
- making a plain-text assignment to all variables that might be changed in non-obvious ways; or
- put the hidden assignments into a function and return the variables from the function (but this can just postpone the problem); or
- Don't Do That -- don't make hidden assignments to variables. For example, assign to fields of a struct inside a function and return the struct and access the fields; or
- use a class with static methods to create what are effectively constants
3 Comments
Leo Simon
on 2 Aug 2020
per isakson
on 2 Aug 2020
"This is defined behavior for the execution engine for the last few releases" I don't find it in the on-line Help Center. Where should I look?
I also know that MATHWORKS would prefer me not to define variables that clash with their own builtins. However, the code still should work properly
I don't know why you think it should work properly. That is expected behavior and is one of the chief reasons eval is a bad idea. Also, it is unclear how you are executing your code and what errors you see. In particular, you say the 2nd and 4th last lines throw errors. But that should be impossible. If the 4th last line threw an error, the code should never reach the 2nd last line.
One easy solution is to use structs:
function nothing
S.rho = 0.8;
S.phi = 0.8;
S.gamma = 0.6;
S.eta = 0.05;
S.mu = .5;
Params = {'phi','rho','gamma'}; %subset
nParam = numel(Params);
for ii=1:nParam;
T.(Params{ii})=S.(Params{ii});
end;
class(T.rho)
class(T.gamma)
T.gamma
3*T.gamma
keyboard;
Obviously, if the goals is to copy all the fields in S, you don't need to use a for-loop. You could just do
T=S;
dpb
on 2 Aug 2020
From the doc--
Whenever possible, do not include output arguments within the input to the eval function,
such as eval(['output = ',expression]). The preferred syntax,
output = eval(expression)
allows the MATLAB parser to perform stricter checks on your code, preventing untrapped errors
and other unexpected behavior.
I don't see anything here that indicates any need for eval anyway over simply writing the explicit assignments; there are only five variables and it took four lines of code to write the loop and how much time and frustration debugging?
7 Comments
John D'Errico
on 2 Aug 2020
10 upvotes to this response.
per isakson
on 2 Aug 2020
The question starts: "In the MWE below, "
dpb
on 2 Aug 2020
_The question starts: "In the MWE below, " _
I saw that too, and had/have no klew what it means?
per isakson
on 2 Aug 2020
MWE stands for Minimal working example. Occasionally we ask for a MWE when the question includes a lot of irrelevant code. In that respect OP made an effort and ended up with only five variables.
Bruno Luong
on 3 Aug 2020
Edited: Bruno Luong
on 3 Aug 2020
A true solution is MATLAB would allow users to define constants, not variable with unchaged value, similar to macro in C; the constant can be visible within local/global scope.
Might be the JIT accelerator does it already internally (?) who knows, as they said speed optimization is their business.
For sure it would be nice where as that kind of optimization occurs or not, at least for professional programmer. ;-)
Walter Roberson
on 3 Aug 2020
some day I should test..
It is possible to subsasgn in the middle of an expression, and if I recall correctly it does change a variable in that position (that is, the new value is used in later parts of the expression.) This is similar to the way that assignin('caller') can affect a variable in an expression. I have tested these a bit for variables.
But at the moment I do not recall that I have tested for hard-coded constants: their slot can be assigned to using subsref and anonymous functions, but does that change other copies of the same literal in the line? (Though I seem to recall that I did test that once and found it did not happen, unlike the classical FORTRAN bug in early days where you could modify constants)
Bruno Luong
on 2 Aug 2020
Edited: Bruno Luong
on 2 Aug 2020
1 vote
This is NOT a bug but documenented behavior from recent release
"If you intend to use x as a variable from data.mat instead of a function, explicitly declare it. Similarly, to use an identifier x as a variable obtained from a script, declare it before invoking the script. This new behavior also applies if the variable is implicitly introduced by the functions sim, eval, evalc, and assignin. "
The workaround as explained in this doc would be you "declare" your variables, e.g. initialize with variable name explicitly written down with empty value, then call EVAL to change the values. Then use it later.
I must admit that would defeat somewhat the goal.
Categories
Find more on Startup and Shutdown in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!