Particularly strange bug using the eval function

조회 수: 6 (최근 30일)
Leo Simon
Leo Simon 2020년 8월 1일
편집: per isakson 2020년 8월 3일
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;

채택된 답변

per isakson
per isakson 2020년 8월 2일
편집: per isakson 2020년 8월 2일
I reproduced the issue you report on R2018b, Win10.
I noticed that if
  • I put a breakpoint at the line "class(gamma)"
  • run the function, nothing
  • execute "class(gamma)" by select, right click, Evaluate Selection
  • "class(gamma)" returns the result that you expect
The issue has something to do with the Just-In-Time (JIT) accelerator, I think. You could report it as a bug. (However, DB's answer make me believe they already knows about a bunch of similar cases.)
IMO:
  • Experiments with this kind of code is a waste of time. However, the group working on the JIT should be interested in your observation.
  • "but I don't really see an alternative in this instance." Describe "this instance" and someone here might come up with a better alternative.
  댓글 수: 9
Stephen23
Stephen23 2020년 8월 2일
편집: Stephen23 2020년 8월 2일
"You could report it as a bug"
Not a bug, rather a fix to earlier ambiguous syntaxes. The general problem is NOT just related to changes in R2019b, because these ambiguous syntaxes have existed in many forms:
I would not recommend using any kind of shadow script: that is just sweeping the hackery under the carpet, but does not actually fix the root cause: your "solution" will always be fragile and susceptible to any changes in precedence rules with past and future versions, as well as fragile to any changes in the path, the MATLAB installation, your own code distribution, etc.
The robust solution is to write code that does not magically name variables.
dpb
dpb 2020년 8월 2일
편집: dpb 2020년 8월 2일
The OP's lament that common mthematical variables are builtin MATLAB functions is one of reality and I agree it is unfortunate that Cleve et al., didn't think of naming the functions differently initially to preserve the names for users as variables. But, hindsight is 20:20.
Name spaces or a dictionary something like Forth uses would be one way to be able to reduce some of the namespace pollution problem -- it is getting really crowded in here any more with all the new functionality even in the base space and the few toolboxes I have; by the time one has a full collection it must be getting difficult to not have name clashes.
It's not helped by the recent introduction of the multitude of similar functions for the same thing as were already provided...instead of fixing the existing ones. And, of course, hardly anything ever gets removed.
As for solving OP's problem, I think it is probably best to simply recognize that MATLAB is what MATLAB is and "go with the flow" and give in gracefully. The hacks to beat Mother Nature just aren't worth the effort; it's not like you can't accomplish the end objective otherwise.

댓글을 달려면 로그인하십시오.

추가 답변 (4개)

Walter Roberson
Walter Roberson 2020년 8월 2일
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
per isakson
per isakson 2020년 8월 2일
"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?

댓글을 달려면 로그인하십시오.


Matt J
Matt J 2020년 8월 2일
편집: Matt J 2020년 8월 2일
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;
  댓글 수: 2
Leo Simon
Leo Simon 2020년 8월 2일
Thanks Matt, for various reasons I really don't want to use structs, though I see that it would solve the problem
Matt J
Matt J 2020년 8월 2일
It's hard to see what those reasons might be. There is nothing you can do with indivdual variables that you cannot do with structs. They are basically the same thing, except one has a dot in its name.

댓글을 달려면 로그인하십시오.


dpb
dpb 2020년 8월 2일
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
Bruno Luong
Bruno Luong 2020년 8월 3일
편집: Bruno Luong 2020년 8월 3일
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
Walter Roberson 2020년 8월 3일
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
Bruno Luong 2020년 8월 2일
편집: Bruno Luong 2020년 8월 2일
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.

카테고리

Help CenterFile Exchange에서 Historical Contests에 대해 자세히 알아보기

제품


릴리스

R2019b

Community Treasure Hunt

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

Start Hunting!

Translated by