Thread Subject: eval, feval, global
|
1) Why are eval, feval considered not elegant and banned in
programming contests? I often use eval, why should a good
programmer stay away from it?
2) Same question about GLOBAL.
Yuri
|
|
"Yuri Geshelin" <geshelin@hotmail.com> wrote in message
<fsebf0$43u$1@fred.mathworks.com>...
> 1) Why are eval, feval considered not elegant and banned in
> programming contests? I often use eval, why should a good
> programmer stay away from it?
>
> 2) Same question about GLOBAL.
>
> Yuri
I use eval, well almost never.
If you are using eval to create variable names,
especially sequences of variables, this is poor
coding style. Use an array instead. After all
Matlab is designed to work with arrays. Eval
is evil.
In terms of programming contests, I think I
recall that there were some potential hacking
issues with eval.
As for globals, again, I never use global
variables. I can't think of a use I've had
for them for many years. They can make
debugging an interesting task. And since
all they do is change how variables are
passed around, why bother with them?
Stuff your parameters into a structure that
gets passed around. Use nested functions.
Its like goto. Yes, you can use the construct,
but there are just too many good alternatives,
and the net result of gotos liberally spread
around your code is almost always bad. The
same logic applies to globals. They encourage
sloppy programming.
John
|
|
Hi Yuri,
EVAL should rarely be used. Most of the time, there's a
much more efficient way to accomplish the same thing. Here
are some notes on this subject:
http://www.mathworks.com/support/tech-notes/1100/1103.html
The use of global variables is debatable, but there's
nothing at all wrong with FEVAL. In fact, there are some
nice programming patterns that you can implement no other way.
All that being said, the reason these are banned from the
contest has nothing to do with good programming practices.
These constructs are banned to increase the security in the
environment which automatically scores the entries.
Thanks for your interest,
Matthew
|
|
"Yuri Geshelin" <geshelin@hotmail.com> writes:
> 1) Why are eval, feval considered not elegant and banned in
> programming contests? I often use eval, why should a good programmer
> stay away from it?
It is hard for MATLAB to accelerate (slow) and it creates hard-to-read
code. There is usually an alternative with dynamic field names of
structures, arrays, cell arrays, etc.
If there's a code pattern you think you need eval for, post it here, and
see if we can help.
-Peter
|
|
Thanks to everyone who replied. I agree with John about
GLOBAL.
As for EVAL, as Peter suggested, let me show you my code,
which I found useful.
(Actually, there are two situations, in which I use EVAL,
but I will start with presenting the 1st one, as it is
perhaps the more controversial.)
Suppose we have 2 arrays of equal length:
a = [15 23 35 43];
b = [ 2 11 8 3];
and we need an algorithm, which constructs output array c
as follows:
c = [15 15 <23 repeated 11 times> <35 repeated 8 times> 43
43 43]
Here is a simple function construct_array1, which
accomplishes this task.
% *************************** top of construct_array1
function c = construct_array1(a,b)
c = [];
for i = 1 : length(a)
c = [c ones(1,b(i))*a(i)];
end
% *************************** end of construct_array1
And here is another function construct_array, which
accomplishes the same task.
% *************************** top of construct_array
function c = construct_array(a,b)
if length(a) ~= length(b)
error('arrays a and b should be of equal lengths!')
end
k = floor(log10(max(a)))+1;
i = floor(log10(max(b)))+1;
n = length(a);
m = ones(n,1);
c = eval(['[' ...
reshape([char([m*'o' m*'n' m*'e' m*'s' m*'(' m*'1'
m*',']) ...
reshape(sprintf(['%' num2str(i) 'd'],b),i,n)' ...
char([m*')' m*'*']) ...
reshape(sprintf(['%' num2str(k) 'd'],a),k,n)' ...
char(m*' ')]',1,n*(k+i+10)) ']']);
% *************************** end of construct_array
Think this is crazy? However, let us test both functions:
>> t = cputime; construct_array1(1:10,1000001:1000010);
cputime-t
ans =
1.1875
>> t = cputime; construct_array(1:10,1000001:1000010);
cputime-t
ans =
0.4531
(I was afraid that matlab would crash, but it did not.)
This was experimented on
MATLAB Version 7.0.4.365 (R14) Service Pack 2
Operating System: Microsoft Windows XP Version 5.1 (Build
2600: Service Pack 2)
Pentuim(R) 4 CPU 3.20 GHz, 1.00 GB of RAM.
My implementation is thus 2.6 times faster than the use of
a loop.
I admit that this can be costly in terms of RAM, because
the text string supplied to eval can
be huge. But in my practice, the arrays are not too long
(way shorter than in the test case).
The cpu time is saved because I call my function many times
in a loop.
Yuri
|
|
Just realized that I could have written
char(m*'ones(1,')
instead of
char([m*'o' m*'n' m*'e' m*'s' m*'(' m*'1' m*','])
but this is a minor issue here.
Yuri
|
|
"Yuri Geshelin" <geshelin@hotmail.com> wrote in message
<fsf3ss$e2e$1@fred.mathworks.com>...
> Thanks to everyone who replied. I agree with John about
> GLOBAL.
>
> As for EVAL, as Peter suggested, let me show you my code,
> which I found useful.
>
> (Actually, there are two situations, in which I use EVAL,
> but I will start with presenting the 1st one, as it is
> perhaps the more controversial.)
>
> Suppose we have 2 arrays of equal length:
>
> a = [15 23 35 43];
> b = [ 2 11 8 3];
>
> and we need an algorithm, which constructs output array c
> as follows:
>
> c = [15 15 <23 repeated 11 times> <35 repeated 8 times> 43
> 43 43]
>
> Here is a simple function construct_array1, which
> accomplishes this task.
>
> % *************************** top of construct_array1
> function c = construct_array1(a,b)
>
> c = [];
> for i = 1 : length(a)
> c = [c ones(1,b(i))*a(i)];
> end
>
> % *************************** end of construct_array1
>
> And here is another function construct_array, which
> accomplishes the same task.
>
> % *************************** top of construct_array
> function c = construct_array(a,b)
>
> if length(a) ~= length(b)
> error('arrays a and b should be of equal lengths!')
> end
>
> k = floor(log10(max(a)))+1;
> i = floor(log10(max(b)))+1;
> n = length(a);
> m = ones(n,1);
> c = eval(['[' ...
> reshape([char([m*'o' m*'n' m*'e' m*'s' m*'(' m*'1'
> m*',']) ...
> reshape(sprintf(['%' num2str(i) 'd'],b),i,n)' ...
> char([m*')' m*'*']) ...
> reshape(sprintf(['%' num2str(k) 'd'],a),k,n)' ...
> char(m*' ')]',1,n*(k+i+10)) ']']);
> % *************************** end of construct_array
>
> Think this is crazy? However, let us test both functions:
>
> >> t = cputime; construct_array1(1:10,1000001:1000010);
> cputime-t
>
> ans =
>
> 1.1875
>
> >> t = cputime; construct_array(1:10,1000001:1000010);
> cputime-t
>
> ans =
>
> 0.4531
>
> (I was afraid that matlab would crash, but it did not.)
>
> This was experimented on
>
> MATLAB Version 7.0.4.365 (R14) Service Pack 2
> Operating System: Microsoft Windows XP Version 5.1 (Build
> 2600: Service Pack 2)
> Pentuim(R) 4 CPU 3.20 GHz, 1.00 GB of RAM.
>
> My implementation is thus 2.6 times faster than the use of
> a loop.
> I admit that this can be costly in terms of RAM, because
> the text string supplied to eval can
> be huge. But in my practice, the arrays are not too long
> (way shorter than in the test case).
> The cpu time is saved because I call my function many times
> in a loop.
>
> Yuri
>
Hi Yuri,
I haven't tested exactly how much, but I'm sure
preallocating your result array will significantly speed up
the non-eval version of the function.
Eg:
startIdx = 1;
c = zeros(1,sum(b));
for i = 1:length(b)
c((0:b(i)-1) + startIdx) = ones(1,b(i))*a(i);
startIdx = startIdx + b(i);
end
And I'm sure this can be improved further still.
Cheers,
Sven.
|
|
"Sven " <sven.holcombe@gmail.deleteme.com> wrote in message
<fsf7da$g1$1@fred.mathworks.com>...
> "Yuri Geshelin" <geshelin@hotmail.com> wrote in message
> <fsf3ss$e2e$1@fred.mathworks.com>...
> > Thanks to everyone who replied. I agree with John about
> > GLOBAL.
> >
> > As for EVAL, as Peter suggested, let me show you my code,
> > which I found useful.
> >
> > (Actually, there are two situations, in which I use EVAL,
> > but I will start with presenting the 1st one, as it is
> > perhaps the more controversial.)
> >
> > Suppose we have 2 arrays of equal length:
> >
> > a = [15 23 35 43];
> > b = [ 2 11 8 3];
> >
> > and we need an algorithm, which constructs output array c
> > as follows:
> >
> > c = [15 15 <23 repeated 11 times> <35 repeated 8 times> 43
> > 43 43]
> >
> > Here is a simple function construct_array1, which
> > accomplishes this task.
> >
> > % *************************** top of construct_array1
> > function c = construct_array1(a,b)
> >
> > c = [];
> > for i = 1 : length(a)
> > c = [c ones(1,b(i))*a(i)];
> > end
> >
> > % *************************** end of construct_array1
> >
> > And here is another function construct_array, which
> > accomplishes the same task.
> >
> > % *************************** top of construct_array
> > function c = construct_array(a,b)
> >
> > if length(a) ~= length(b)
> > error('arrays a and b should be of equal lengths!')
> > end
> >
> > k = floor(log10(max(a)))+1;
> > i = floor(log10(max(b)))+1;
> > n = length(a);
> > m = ones(n,1);
> > c = eval(['[' ...
> > reshape([char([m*'o' m*'n' m*'e' m*'s' m*'(' m*'1'
> > m*',']) ...
> > reshape(sprintf(['%' num2str(i) 'd'],b),i,n)' ...
> > char([m*')' m*'*']) ...
> > reshape(sprintf(['%' num2str(k) 'd'],a),k,n)' ...
> > char(m*' ')]',1,n*(k+i+10)) ']']);
> > % *************************** end of construct_array
> >
> > Think this is crazy? However, let us test both functions:
> >
> > >> t = cputime; construct_array1(1:10,1000001:1000010);
> > cputime-t
> >
> > ans =
> >
> > 1.1875
> >
> > >> t = cputime; construct_array(1:10,1000001:1000010);
> > cputime-t
> >
> > ans =
> >
> > 0.4531
> >
> > (I was afraid that matlab would crash, but it did not.)
> >
> > This was experimented on
> >
> > MATLAB Version 7.0.4.365 (R14) Service Pack 2
> > Operating System: Microsoft Windows XP Version 5.1 (Build
> > 2600: Service Pack 2)
> > Pentuim(R) 4 CPU 3.20 GHz, 1.00 GB of RAM.
> >
> > My implementation is thus 2.6 times faster than the use of
> > a loop.
> > I admit that this can be costly in terms of RAM, because
> > the text string supplied to eval can
> > be huge. But in my practice, the arrays are not too long
> > (way shorter than in the test case).
> > The cpu time is saved because I call my function many times
> > in a loop.
> >
> > Yuri
> >
>
>
> Hi Yuri,
>
> I haven't tested exactly how much, but I'm sure
> preallocating your result array will significantly speed up
> the non-eval version of the function.
>
> Eg:
>
> startIdx = 1;
> c = zeros(1,sum(b));
> for i = 1:length(b)
> c((0:b(i)-1) + startIdx) = ones(1,b(i))*a(i);
> startIdx = startIdx + b(i);
> end
>
> And I'm sure this can be improved further still.
Well I took the couple of mins to test this. I stand
corrected. My function is:
startIdx = 1;
c = zeros(1,sum(b));
for i = 1:length(b)
c((0:b(i)-1) + startIdx) = a(i);
startIdx = startIdx + b(i);
end
Running above function 100 times:
>> tic; for i=1:100, temp_function(1:12,500000:500011); end, toc
Elapsed time is 44.180691 seconds.
Running with the eval version you posted:
>> tic; for i=1:100, temp_function(1:12,500000:500011); end, toc
Elapsed time is 18.499473 seconds.
Still much faster avoiding the loop...
Cheers,
Sven.
|
|
"Yuri Geshelin" <geshelin@hotmail.com> wrote in message >
Suppose we have 2 arrays of equal length:
>
> a = [15 23 35 43];
> b = [ 2 11 8 3];
>
> and we need an algorithm, which constructs output array c
> as follows:
>
> c = [15 15 <23 repeated 11 times> <35 repeated 8 times> 43
> 43 43]
>
> Here is a simple function construct_array1, which
> accomplishes this task.
Try the function MMREPEAT from the File Exchange. It does
exactly this and does not use EVAL. Avoid EVAL!
Compare its speed to your two functions.
http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=1024&objectType=file
Duane Hanselman
|
|
"Yuri Geshelin" <geshelin@hotmail.com> wrote in message >
Suppose we have 2 arrays of equal length:
>
> a = [15 23 35 43];
> b = [ 2 11 8 3];
>
> and we need an algorithm, which constructs output array c
> as follows:
>
> c = [15 15 <23 repeated 11 times> <35 repeated 8 times> 43
> 43 43]
>
> Here is a simple function construct_array1, which
> accomplishes this task.
Try the function MMREPEAT from the File Exchange. It does
exactly this and does not use EVAL. Avoid EVAL!
Compare its speed to your two functions.
http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=1024&objectType=file
Duane Hanselman
|
|
"Yuri Geshelin" <geshelin@hotmail.com> wrote in message >
Suppose we have 2 arrays of equal length:
>
> a = [15 23 35 43];
> b = [ 2 11 8 3];
>
> and we need an algorithm, which constructs output array c
> as follows:
>
> c = [15 15 <23 repeated 11 times> <35 repeated 8 times> 43
> 43 43]
>
> Here is a simple function construct_array1, which
> accomplishes this task.
Try the function MMREPEAT from the File Exchange. It does
exactly this and does not use EVAL. Avoid EVAL!
Compare its speed to your two functions.
http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=1024&objectType=file
Duane Hanselman
|
|
"Yuri Geshelin" <geshelin@hotmail.com> wrote in message >
Suppose we have 2 arrays of equal length:
>
> a = [15 23 35 43];
> b = [ 2 11 8 3];
>
> and we need an algorithm, which constructs output array c
> as follows:
>
> c = [15 15 <23 repeated 11 times> <35 repeated 8 times> 43
> 43 43]
>
> Here is a simple function construct_array1, which
> accomplishes this task.
Try the function MMREPEAT from the File Exchange. It does
exactly this and does not use EVAL. Avoid EVAL!
Compare its speed to your two functions.
http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=1024&objectType=file
Duane Hanselman
|
|
"Duane Hanselman" <masteringmatlab@yahoo.spam.com> wrote in message
<fsfrld$fep$1@fred.mathworks.com>...
> "Yuri Geshelin" <geshelin@hotmail.com> wrote in message >
> Suppose we have 2 arrays of equal length:
> >
> > a = [15 23 35 43];
> > b = [ 2 11 8 3];
> >
> > and we need an algorithm, which constructs output array c
> > as follows:
> >
> > c = [15 15 <23 repeated 11 times> <35 repeated 8 times> 43
> > 43 43]
> >
> > Here is a simple function construct_array1, which
> > accomplishes this task.
>
> Try the function MMREPEAT from the File Exchange. It does
> exactly this and does not use EVAL. Avoid EVAL!
>
> Compare its speed to your two functions.
>
> http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?
objectId=1024&objectType=file
>
>
> Duane Hanselman
It seems ironic that Duane's response about
mmrepeat got repeated by the website. 8-)
The point I'll make is that Yuri's problem,
which he solves using eval in a rather
inelegant way, is far more efficiently
solved using mmrepeat.
Eval is very rarely necessary. (As opposed
to feval, which I'll agree does have some
uses.)
John
|
|
>
> It seems ironic that Duane's response about
> mmrepeat got repeated by the website. 8-)
Sorry about that. My browser froze up and I had to kill it
to regain control of my computer. Somehow in the process,
multiple submissions were made. I take responsibility and am
sorry for the inconvenience.
Duane
|
|
Although mmrepeat is elegent, for Yuri's test case it is
almost as slow as Yuri's original (inefficient) loop.
I suspect that timing for this is highly data dependent
(i.e. depends heavily on the values in the second vector),
however for me (and Yuri's test case) the following loop
significantly beats all curently proposed approaches:
%%%%%%% Cut here
function c = construct_array4(a,b)
eIdx = cumsum(b);
sIdx = [1 eIdx(1:end-1)+1];
c = nan(1,eIdx(end));
for idx = 1:length(eIdx)
c(sIdx(idx):eIdx(idx)) = a(idx);
end
%%%%%%% Cut here
However, the bottom line is still that EVAL can and should
be avoided.
Phil.
|
|
Many thanks to everyone who replied.
The fastest algorithm is Phil's. Assuming that its time is
unity, here is the cpu time summary of the following test
case:
t = cputime; <code_name>(1:10,1000001:1000010); <code_time>
= cputime-t;
-------------------------------------
Phil's construct_array4.m - 1.00
My awkward EVAL implementation (construct_array.m) - 1.67
mmrepeat - 3.40
Sven's code - 3.47
My original version (no preallocation) - 4.00
------------------------------------
You convinced me that EVAL is beatable.
I will get back to you shortly and present another
situation, in which I make use of eval.
|
|
"Phil Goddard" <philgoddardNOSPAM@telus.net> wrote in
message <fsg1fl$p8e$1@fred.mathworks.com>...
>
> Although mmrepeat is elegent, for Yuri's test case it is
> almost as slow as Yuri's original (inefficient) loop.
This is interesting. I wrote mmrepeat before JIT existed. So
it was the best then, but now JIT and a good FOR loop is faster!
Needless to say, I am not going to go back and revise every
piece of code I have to find these differences!
Thanks everyone for the good thread.
|
|
"Yuri Geshelin" <geshelin@hotmail.com> writes:
> Many thanks to everyone who replied.
>
> The fastest algorithm is Phil's. Assuming that its time is
> unity, here is the cpu time summary of the following test
> case:
>
> t = cputime; <code_name>(1:10,1000001:1000010); <code_time>
> = cputime-t;
>
> -------------------------------------
>
> Phil's construct_array4.m - 1.00
> My awkward EVAL implementation (construct_array.m) - 1.67
> mmrepeat - 3.40
> Sven's code - 3.47
> My original version (no preallocation) - 4.00
>
> ------------------------------------
>
> You convinced me that EVAL is beatable.
That said, sometimes you have work to do, and a solution with eval leaps
to mind. Great. Use it and get on with life. Maybe that solution
takes less time to write/maintain than some other fancier vectorizing
thing. In your case, I would suggest leaving your simple loop in place
in a comment to illustrate what is happening, with a note that the eval
version is inexplicably faster.
In 98% of the cases we see here, the use of eval is due to a failure to
understand arrays, the difference between literal strings and string
variables, etc. These cases are easily correctable, and the switch away
from eval leads to code which is better by any and all metrics.
-Peter
|
|
All right, here is the second typical situation, in which I
use EVAL. It is not about computational cost, just a
programming habit.
I have PLOT in the body of my function, and sometimes I
want to do
plot(A.TEMP)
at other times it would be another text string:
plot(A.PSAL)
At the time of the call to the function I do not know,
which field should be plotted.
So I pass the array of text strings in the function (it
gets passed for other reasons, too):
prop = {'TEMP', 'PSAL', ...........}
And once I compute the index of the required field (irf), I
do this:
eval(['plot(A.' prop{irf} ')'])
Clumsy, but I got very much used to it. What would be your
recommendation?
(I wanted to stay away from CASE, because the number of
fields is quite big).
Thanks,
Yuri
|
|
"Yuri Geshelin":
<SNIP evil eval evergreen...
> eval(['plot(A.' prop{irf} ')'])
> What would be your recommendation
one of the solutions
a.b=1:10;
a.cc=1+a.b;
a.ddd=2+a.b;
f={'b','cc','ddd'};
plot(a.(f{1}));
hold on;
plot(a.(f{2}));
us
|
|
"us " <us@neurol.unizh.ch> wrote in message <fsgms3$nv8
$1@fred.mathworks.com>...
> "Yuri Geshelin":
> <SNIP evil eval evergreen...
>
> > eval(['plot(A.' prop{irf} ')'])
> > What would be your recommendation
>
> one of the solutions
>
> a.b=1:10;
> a.cc=1+a.b;
> a.ddd=2+a.b;
> f={'b','cc','ddd'};
> plot(a.(f{1}));
> hold on;
> plot(a.(f{2}));
>
> us
Thanks us,
for filling yet another gap in my knowldege of matlab. I
did not know that a.(f{3}) is a valid reference to a
structure's field. No more questions.
Yuri
|
|
"Yuri Geshelin" <geshelin@hotmail.com> wrote in message
<fsgmf0$fcd$1@fred.mathworks.com>...
> All right, here is the second typical situation, in which I
> use EVAL. It is not about computational cost, just a
> programming habit.
>
>
> I have PLOT in the body of my function, and sometimes I
> want to do
>
>
> plot(A.TEMP)
>
>
> at other times it would be another text string:
>
>
> plot(A.PSAL)
>
>
> At the time of the call to the function I do not know,
> which field should be plotted.
> So I pass the array of text strings in the function (it
> gets passed for other reasons, too):
>
>
> prop = {'TEMP', 'PSAL', ...........}
>
>
> And once I compute the index of the required field (irf), I
> do this:
>
>
> eval(['plot(A.' prop{irf} ')'])
>
>
> Clumsy, but I got very much used to it. What would be your
> recommendation?
>
>
> (I wanted to stay away from CASE, because the number of
> fields is quite big).
Another sloppy programming habit, encouraged
by eval.
Use getfield, or better, dynamic field names.
John
|
|
...to come up with annother part of the original topic:
As I have to work with code from guys, who do not bother to
use 'clear global', I have started to use
appdata(0,...)
with a structure instead of global variables in code
without GUI. Anybody knows about problems using it?
Regards,
Rainer
PS: I agree on 'eval' as bad style and that it can be
replaced, even when I use str2num with GUI programming
quite often (which uses 'eval' ;o) - ).
'feval' I see frequently in "Libraries", which wrap several
functions into one file - are there better alternatives out
there.
|
|
Hi,
I think the problem can be the same as in the case of
GLOBAL. As Matthew mentioned, it can make debugging an
interesting task. I realize this now. You secure yourself
from the guys who do not bother to use 'clear global', but
you are still likely to make your own mistakes when there
are dozens of variables are passed in many functions. In
this sense, it's just another form of GLOBAL.
Yuri
"sfreeman " <crush35@gmx.net> wrote in message
<fsinpi$e2q$1@fred.mathworks.com>...
> ...to come up with annother part of the original topic:
>
> As I have to work with code from guys, who do not bother
to
> use 'clear global', I have started to use
> appdata(0,...)
> with a structure instead of global variables in code
> without GUI. Anybody knows about problems using it?
>
> Regards,
> Rainer
>
> PS: I agree on 'eval' as bad style and that it can be
> replaced, even when I use str2num with GUI programming
> quite often (which uses 'eval' ;o) - ).
> 'feval' I see frequently in "Libraries", which wrap
several
> functions into one file - are there better alternatives
out
> there.
|
|
"Yuri Geshelin" <geshelin@hotmail.com> wrote in message
<fsebf0$43u$1@fred.mathworks.com>...
> 1) Why are eval, feval considered not elegant and banned
in
> programming contests? I often use eval, why should a good
> programmer stay away from it?
>
> 2) Same question about GLOBAL.
>
> Yuri
>
Sorry for bringing this up again. It's about the second
issue: the use of GLOBAL. When John said that it "can make
debugging an interesting task", I thought I had grasped the
subject and agreed with him. But now I think I
misunderstand something.
Let me present you with a situation, which I ran across. It
probably has to do with John’s remark. I was writing a GUI.
It took me a while, I would put it off and get back to it
in months. I was using dozens of GLOBAL variables. One day
I realized that I needed to fix a bug in a button’s
callback. I quickly wrote up a function, which takes care
of the glitch. This should have been very simple, but I got
an error message at the stage, when I least expected it.
Here is what happened. I had global variable PROP and
forgot about it, because I wasn’t working on my GUI for a
month. Now, in order to accomplish the task at hand, I
happened to use variable PROP again, but for a totally
different purpose... And again, I declared it as global. No
comments.....
This was what I recalled when John said that globals "can
make debugging an interesting task". I do realize that this
is a potential source of errors. But in terms of my
example, please explain me how exactly I should follow
these John’s recommendations:
"Stuff your parameters into a structure that gets passed
around. Use nested functions."
I know how to use CLASS, if this is of any help. I just
don't see how this would prevent me from making the same
mistake.
Any comments on John D’Errico’s message of 26 March are
appreciated.
Thanks,
Yuri
|
|
"Yuri Geshelin" <geshelin@hotmail.com> wrote in message
news:fsp7th$j9u$1@fred.mathworks.com...
> "Yuri Geshelin" <geshelin@hotmail.com> wrote in message
> <fsebf0$43u$1@fred.mathworks.com>...
>> 1) Why are eval, feval considered not elegant and banned
> in
>> programming contests? I often use eval, why should a good
>> programmer stay away from it?
>>
>> 2) Same question about GLOBAL.
>>
>> Yuri
>>
>
> Sorry for bringing this up again. It's about the second
> issue: the use of GLOBAL. When John said that it "can make
> debugging an interesting task", I thought I had grasped the
> subject and agreed with him. But now I think I
> misunderstand something.
Let's say you write a function, foo, that uses a global variable x. It
works perfectly fine on its own.
Now I write a function, baz, that uses a global variable x. It too works
perfectly fine on its own.
Finally, someone else finds your foo function and my baz function, which are
perfect for the application he or she is writing. That person creates a
function, foobarbaz that uses your foo function and my baz function. They
find that the foobarbaz function doesn't work, and are confused as to why it
doesn't work. Each of the subfunctions works well on their own, and each
piece of code in the subfunctions that work with the x variable appear to be
working correctly.
Now this is a small example, with just three functions involved. It
wouldn't be _that_ hard to notice that both subfunctions are working with
the same global variable. Now picture a larger project, where there are ten
or twenty functions involved. Tracing how each one interacts with the
others via global variables will quickly become messy.
> Let me present you with a situation, which I ran across. It
> probably has to do with John’s remark. I was writing a GUI.
> It took me a while, I would put it off and get back to it
> in months. I was using dozens of GLOBAL variables. One day
> I realized that I needed to fix a bug in a button’s
> callback. I quickly wrote up a function, which takes care
> of the glitch. This should have been very simple, but I got
> an error message at the stage, when I least expected it.
> Here is what happened. I had global variable PROP and
> forgot about it, because I wasn’t working on my GUI for a
> month. Now, in order to accomplish the task at hand, I
> happened to use variable PROP again, but for a totally
> different purpose... And again, I declared it as global. No
> comments.....
Don't worry. I don't think you're alone in having done that at one time or
another.
> This was what I recalled when John said that globals "can
> make debugging an interesting task". I do realize that this
> is a potential source of errors. But in terms of my
> example, please explain me how exactly I should follow
> these John’s recommendations:
>
> "Stuff your parameters into a structure that gets passed
> around. Use nested functions."
If all of your parameters are stored in a struct array that get passed from
function to function, any change to the parameters have to be made to the
struct array and returned from those functions, so you know where and when
those parameters were changed. That helps in debugging.
In the example I posted above, if foobarfaz has to pass parameters as inputs
to foo and baz, then any changes to the parameters in foo won't affect baz
unless they are returned from foo and the modified parameters passed into
baz.
--
Steve Lord
slord@mathworks.com
|
|
On Mar 30, 8:18=A0pm, "Steven Lord" <sl...@mathworks.com> wrote:
> "Yuri Geshelin" <geshe...@hotmail.com> wrote in message
>
> news:fsp7th$j9u$1@fred.mathworks.com...
>
>
>
>
>
> > "Yuri Geshelin" <geshe...@hotmail.com> wrote in message
> > <fsebf0$43...@fred.mathworks.com>...
> >> 1) Why are eval, feval considered not elegant and banned
> > in
> >> programming contests? I often use eval, why should a good
> >> programmer stay away from it?
>
> >> 2) Same question about GLOBAL.
>
> >> Yuri
>
> > Sorry for bringing this up again. It's about the second
> > issue: the use of GLOBAL. When John said that it "can make
> > debugging an interesting task", I thought I had grasped the
> > subject and agreed with him. But now I think I
> > misunderstand something.
>
> Let's say you write a function, foo, that uses a global variable x. =A0It
> works perfectly fine on its own.
>
> Now I write a function, baz, that uses a global variable x. =A0It too work=
s
> perfectly fine on its own.
>
> Finally, someone else finds your foo function and my baz function, which a=
re
> perfect for the application he or she is writing. =A0That person creates a=
> function, foobarbaz that uses your foo function and my baz function. =A0Th=
ey
> find that the foobarbaz function doesn't work, and are confused as to why =
it
> doesn't work. =A0Each of the subfunctions works well on their own, and eac=
h
> piece of code in the subfunctions that work with the x variable appear to =
be
> working correctly.
>
> Now this is a small example, with just three functions involved. =A0It
> wouldn't be _that_ hard to notice that both subfunctions are working with
> the same global variable. =A0Now picture a larger project, where there are=
ten
> or twenty functions involved. =A0Tracing how each one interacts with the
> others via global variables will quickly become messy.
>
> > Let me present you with a situation, which I ran across. It
> > probably has to do with John’s remark. I was writing a GUI.
> > It took me a while, I would put it off and get back to it
> > in months. I was using dozens of GLOBAL variables. One day
> > I realized that I needed to fix a bug in a button’s
> > callback. I quickly wrote up a function, which takes care
> > of the glitch. This should have been very simple, but I got
> > an error message at the stage, when I least expected it.
> > Here is what happened. I had global variable PROP and
> > forgot about it, because I wasn’t working on my GUI for a
> > month. Now, in order to accomplish the task at hand, I
> > happened to use variable PROP again, but for a totally
> > different purpose... And again, I declared it as global. No
> > comments.....
>
> Don't worry. =A0I don't think you're alone in having done that at one time=
or
> another.
>
> > This was what I recalled when John said that globals "can
> > make debugging an interesting task". I do realize that this
> > is a potential source of errors. But in terms of my
> > example, please explain me how exactly I should follow
> > these John’s recommendations:
>
> > "Stuff your parameters into a structure that gets passed
> > around. Use nested functions."
>
> If all of your parameters are stored in a struct array that get passed fro=
m
> function to function, any change to the parameters have to be made to the
> struct array and returned from those functions, so you know where and when=
> those parameters were changed. =A0That helps in debugging.
>
> In the example I posted above, if foobarfaz has to pass parameters as inpu=
ts
> to foo and baz, then any changes to the parameters in foo won't affect baz=
> unless they are returned from foo and the modified parameters passed into
> baz.
>
> --
> Steve Lord
> sl...@mathworks.com- Hide quoted text -
>
> - Show quoted text -
----------------------------------------------------------------
But Steven, can you pass your structure variable into a callback
function for a button on your GUI? As far as I know the answer is
No. You're limited to the 3 default callback arguments (hObject,
etc.). Let's say your callback function needs to use some variable
that has been set by some other function in your program. It's this
situation where I use global variables because I don't know of other
anyway to get variables into the function (i.e. ones that aren't
retrievable from one of the other GUI controls). If there is a way to
get variables into a callback function without using global variables,
let me know.
Thanks,
ImageAnalyst
|
|
In article
<5f332995-1a52-4cb2-bf08-59807891b2bf@l42g2000hsc.googlegroups.com>,
ImageAnalyst <imageanalyst@mailinator.com> wrote:
[snip]
> But Steven, can you pass your structure variable into a callback
> function for a button on your GUI? As far as I know the answer is
> No. You're limited to the 3 default callback arguments (hObject,
> etc.). Let's say your callback function needs to use some variable
> that has been set by some other function in your program. It's this
> situation where I use global variables because I don't know of other
> anyway to get variables into the function (i.e. ones that aren't
> retrievable from one of the other GUI controls). If there is a way to
> get variables into a callback function without using global variables,
> let me know.
> Thanks,
> ImageAnalyst
Sure there is: the old way (using the UserData of the GUI figure) and
the new way (using nested functions). The following two functions do
the same thing:
%-------------- demogui2.m --------------------------------
function demogui2(cmd)
if nargin == 0
cmd = 'init';
end
switch cmd
case 'init' % Initialize GUI application
myname = mfilename;
fig = figure('Position',[0 0 150 90],...
'CreateFcn',{@movegui,'center'},...
'NumberTitle','off',...
'Name',myname,...
'CloseRequestFcn',[myname,' close'],...
'Resize','off');
info.count = 1;
uicontrol('Style','pushbutton',...
'Position',[20 20 50 20],...
'String','Down',...
'CallBack',[myname,' dec'])
uicontrol('Style','pushbutton',...
'Position',[80 20 50 20],...
'String','Up',...
'CallBack',[myname,' inc'])
info.editbox = uicontrol('Style','edit',...
'Position',[45 50 60 20],...
'String',num2str(info.count),...
'CallBack',[myname,' edit']);
set(fig,'UserData',info,...
'HandleVisibility','callback')
case 'dec' % Decrement counter
fig = gcbf;
info = get(fig,'UserData');
info.count = info.count - 1;
set(info.editbox,'String',num2str(info.count))
set(fig,'UserData',info)
case 'inc' % Increment counter
fig = gcbf;
info = get(fig,'UserData');
info.count = info.count + 1;
set(info.editbox,'String',num2str(info.count))
set(fig,'UserData',info)
case 'edit' % Enter value in edit box
fig = gcbf;
info = get(fig,'UserData');
newcount = sscanf(get(info.editbox,'String'),'%d');
if ~isempty(newcount)
info.count = newcount;
end
set(info.editbox,'String',num2str(info.count))
set(fig,'UserData',info)
case 'close' % Close requested
fig = gcbf;
myname = get(fig,'Name');
answer = questdlg(['Really close ',myname,'?'],...
'','Yes','No','No');
if strcmp(answer,'Yes')
delete(fig)
end
end
%----------------------------------------------------------
%-------------- demogui2_new.m ----------------------------
function demogui2_new
fig = figure('Position',[0 0 150 90],...
'CreateFcn',{@movegui,'center'},...
'NumberTitle','off',...
'Name',mfilename,...
'CloseRequestFcn',@close,...
'Resize','off');
count = 1;
uicontrol('Style','pushbutton',...
'Position',[20 20 50 20],...
'String','Down',...
'CallBack',@dec)
uicontrol('Style','pushbutton',...
'Position',[80 20 50 20],...
'String','Up',...
'CallBack',@inc)
editbox = uicontrol('Style','edit',...
'Position',[45 50 60 20],...
'String',num2str(count),...
'CallBack',@edit);
set(fig,'HandleVisibility','callback')
% Decrement counter
function dec(varargin)
count = count - 1;
set(editbox,'String',num2str(count))
end
% Increment counter
function inc(varargin)
count = count + 1;
set(editbox,'String',num2str(count))
end
% Enter value in edit box
function edit(varargin)
newcount = sscanf(get(editbox,'String'),'%d');
if ~isempty(newcount)
count = newcount;
end
set(editbox,'String',num2str(count))
end
% Close requested
function close(varargin)
myname = get(fig,'Name');
answer = questdlg(['Really close ',myname,'?'],...
'','Yes','No','No');
if strcmp(answer,'Yes')
delete(fig)
end
end
end
%----------------------------------------------------------
--
Doug Schwarz
dmschwarz&ieee,org
Make obvious changes to get real email address.
|
|
> In the example I posted above, if foobarfaz has to pass
parameters as inputs
> to foo and baz, then any changes to the parameters in foo
won't affect baz
> unless they are returned from foo and the modified
parameters passed into
> baz.
>
Steve, do you suggest to use only input / output args as a
way of communication between functions? This may present
another problem, but first I want to make sure I don't
misunderstand you.
In any case, if all we are talking about is the neat
organization of such communication, then I don't see why I
cannot still use globals in the following way:
global common_block_01 common_block_02 ...
where each of common_block_xx is a structure with many
fields. This way, you focus on what you are doing. To make
it yet more secure, I can name those variables
my_project_common_block_01 my_project_common_block_02 ...
If someone decides to make use of my function, he / she
will simply ignore these globals (or deliberately make use
of them).
(I was raised on fortran :) )
Yuri
|
|
Doug Schwarz <see@sig.for.address.edu> wrote in message
<see-494CA7.22033630032008@71-129-133-66.dollamir.com>...
> In article
> <5f332995-1a52-4cb2-bf08-
59807891b2bf@l42g2000hsc.googlegroups.com>,
> ImageAnalyst <imageanalyst@mailinator.com> wrote:
>
> [snip]
>
> > But Steven, can you pass your structure variable into a
callback
> > function for a button on your GUI? As far as I know
the answer is
> > No. You're limited to the 3 default callback arguments
(hObject,
> > etc.). Let's say your callback function needs to use
some variable
> > that has been set by some other function in your
program. It's this
> > situation where I use global variables because I don't
know of other
> > anyway to get variables into the function (i.e. ones
that aren't
> > retrievable from one of the other GUI controls). If
there is a way to
> > get variables into a callback function without using
global variables,
> > let me know.
> > Thanks,
> > ImageAnalyst
>
>
> Sure there is: the old way (using the UserData of the GUI
figure) and
> the new way (using nested functions). The following two
functions do
> the same thing:
>
>
> %-------------- demogui2.m -------------------------------
-
> function demogui2(cmd)
> if nargin == 0
> cmd = 'init';
> end
>
> switch cmd
> case 'init' % Initialize GUI application
> myname = mfilename;
> fig = figure('Position',[0 0 150 90],...
> 'CreateFcn',{@movegui,'center'},...
> 'NumberTitle','off',...
> 'Name',myname,...
> 'CloseRequestFcn',[myname,' close'],...
> 'Resize','off');
> info.count = 1;
> uicontrol('Style','pushbutton',...
> 'Position',[20 20 50 20],...
> 'String','Down',...
> 'CallBack',[myname,' dec'])
> uicontrol('Style','pushbutton',...
> 'Position',[80 20 50 20],...
> 'String','Up',...
> 'CallBack',[myname,' inc'])
> info.editbox = uicontrol('Style','edit',...
> 'Position',[45 50 60 20],...
> 'String',num2str(info.count),...
> 'CallBack',[myname,' edit']);
> set(fig,'UserData',info,...
> 'HandleVisibility','callback')
>
> case 'dec' % Decrement counter
> fig = gcbf;
> info = get(fig,'UserData');
> info.count = info.count - 1;
> set(info.editbox,'String',num2str(info.count))
> set(fig,'UserData',info)
>
> case 'inc' % Increment counter
> fig = gcbf;
> info = get(fig,'UserData');
> info.count = info.count + 1;
> set(info.editbox,'String',num2str(info.count))
> set(fig,'UserData',info)
>
> case 'edit' % Enter value in edit box
> fig = gcbf;
> info = get(fig,'UserData');
> newcount = sscanf(get(info.editbox,'String'),'%
d');
> if ~isempty(newcount)
> info.count = newcount;
> end
> set(info.editbox,'String',num2str(info.count))
> set(fig,'UserData',info)
>
> case 'close' % Close requested
> fig = gcbf;
> myname = get(fig,'Name');
> answer = questdlg(['Really close ',myname,'?'],...
> '','Yes','No','No');
> if strcmp(answer,'Yes')
> delete(fig)
> end
>
> end
> %---------------------------------------------------------
-
>
>
> %-------------- demogui2_new.m ---------------------------
-
> function demogui2_new
>
> fig = figure('Position',[0 0 150 90],...
> 'CreateFcn',{@movegui,'center'},...
> 'NumberTitle','off',...
> 'Name',mfilename,...
> 'CloseRequestFcn',@close,...
> 'Resize','off');
> count = 1;
> uicontrol('Style','pushbutton',...
> 'Position',[20 20 50 20],...
> 'String','Down',...
> 'CallBack',@dec)
> uicontrol('Style','pushbutton',...
> 'Position',[80 20 50 20],...
> 'String','Up',...
> 'CallBack',@inc)
> editbox = uicontrol('Style','edit',...
> 'Position',[45 50 60 20],...
> 'String',num2str(count),...
> 'CallBack',@edit);
> set(fig,'HandleVisibility','callback')
>
> % Decrement counter
> function dec(varargin)
> count = count - 1;
> set(editbox,'String',num2str(count))
> end
>
> % Increment counter
> function inc(varargin)
> count = count + 1;
> set(editbox,'String',num2str(count))
> end
>
> % Enter value in edit box
> function edit(varargin)
> newcount = sscanf(get(editbox,'String'),'%d');
> if ~isempty(newcount)
> count = newcount;
> end
> set(editbox,'String',num2str(count))
> end
>
> % Close requested
> function close(varargin)
> myname = get(fig,'Name');
> answer = questdlg(['Really close ',myname,'?'],...
> '','Yes','No','No');
> if strcmp(answer,'Yes')
> delete(fig)
> end
> end
>
> end
> %---------------------------------------------------------
-
>
> --
> Doug Schwarz
> dmschwarz&ieee,org
> Make obvious changes to get real email address.
Doug, thanks for sharing very valuable ideas with us. But
it seems that demogui2 is not what ImageAnalyst was asking
for. ImageAnalyst wants to pass a structure, while in
demogui2 it is a text string that gets passed. You can also
pass something like num2str(n) as the part of a text
string, but with structure, it won't work.
In both functions, demogui2 and demogui2_new, nothing at
all gets passed into the body of a separate function. In
the demogui2 example, it does not matter, because you can
still use the same technique to call another function
instead of making a recursive call. As for demogui2_new, it
does not help communication between two functions that
reside in separate files.
Yuri
|
|
> Doug, thanks for sharing very valuable ideas with us. But
> it seems that demogui2 is not what ImageAnalyst was
asking
> for. ImageAnalyst wants to pass a structure, while in
> demogui2 it is a text string that gets passed. You can
also
> pass something like num2str(n) as the part of a text
> string, but with structure, it won't work.
>
> In both functions, demogui2 and demogui2_new, nothing at
> all gets passed into the body of a separate function. In
> the demogui2 example, it does not matter, because you can
> still use the same technique to call another function
> instead of making a recursive call. As for demogui2_new,
it
> does not help communication between two functions that
> reside in separate files.
>
> Yuri
>
Sorry about this. I just realized that I can specify
'CallBack'{@function_name,s})
where s is a structure.
Taking my words back.
Yuri
|
|
In article <fsr197$2mn$1@fred.mathworks.com>,
"Yuri Geshelin" <geshelin@hotmail.com> wrote:
> > Doug, thanks for sharing very valuable ideas with us. But
> > it seems that demogui2 is not what ImageAnalyst was
> asking
> > for. ImageAnalyst wants to pass a structure, while in
> > demogui2 it is a text string that gets passed. You can
> also
> > pass something like num2str(n) as the part of a text
> > string, but with structure, it won't work.
> >
> > In both functions, demogui2 and demogui2_new, nothing at
> > all gets passed into the body of a separate function. In
> > the demogui2 example, it does not matter, because you can
> > still use the same technique to call another function
> > instead of making a recursive call. As for demogui2_new,
> it
> > does not help communication between two functions that
> > reside in separate files.
> >
> > Yuri
> >
>
> Sorry about this. I just realized that I can specify
>
> 'CallBack'{@function_name,s})
>
> where s is a structure.
>
> Taking my words back.
>
> Yuri
Yes, you can do that, but the value of s is fixed at the time of the
generation of the callback so I don't think that's what ImageAnalyst
wants either.
demogui2 demonstrates the standard method of communicating data between
components of a gui, namely via the userdata of the gui figure. It
doesn't matter whether the functions reside in the same m-file or not.
demogui2_new demonstrates the method of writing a gui using nested
functions. In this case, you are correct that these various callback
functions must all be in the same m-file. However, inside one of these
callback functions you can call any other function you want, passing it
any parameters that are part of the workspace of the main gui function.
The only caveat is that you must instantiate any such variables in the
main function, even if just as empty matrices.
If you are having trouble with any of this just post a small example of
something you think must be done with global variables and I'd be glad
to show you how to do it without.
--
Doug Schwarz
dmschwarz&ieee,org
Make obvious changes to get real email address.
|
|
Doug Schwarz <see@sig.for.address.edu> wrote in message
<see-8DB03B.12111531032008@71-129-133-66.dollamir.com>...
> In article <fsr197$2mn$1@fred.mathworks.com>,
> "Yuri Geshelin" <geshelin@hotmail.com> wrote:
>
> Yes, you can do that, but the value of s is fixed at the
time of the
> generation of the callback so I don't think that's what
ImageAnalyst
> wants either.
>
> demogui2 demonstrates the standard method of
communicating data between
> components of a gui, namely via the userdata of the gui
figure. It
> doesn't matter whether the functions reside in the same m-
file or not.
>
> demogui2_new demonstrates the method of writing a gui
using nested
> functions. In this case, you are correct that these
various callback
> functions must all be in the same m-file. However,
inside one of these
> callback functions you can call any other function you
want, passing it
> any parameters that are part of the workspace of the main
gui function.
> The only caveat is that you must instantiate any such
variables in the
> main function, even if just as empty matrices.
>
> If you are having trouble with any of this just post a
small example of
> something you think must be done with global variables
and I'd be glad
> to show you how to do it without.
>
> --
> Doug Schwarz
> dmschwarz&ieee,org
> Make obvious changes to get real email address.
Thanks Doug,
I will simplify my real situation. I have function
refresh_all.
%%%%%%%%%%%% top of refresh_all
function refresh_all
% array of handles to the selected curves whose color has
to be changed:
global final_handles
set(final_handles,'color','red')
%%%%%%%%%%%% bottom of refresh_all
I also have a callback to a button that invokes
refresh_all. The idea is, lots of other multipurpose
functions alter 1-D array final_handles (via global) before
the user clicks the button.
Yuri
|
|
"Yuri Geshelin" <geshelin@hotmail.com> wrote in message
news:fsqs0d$ljr$1@fred.mathworks.com...
>
>> In the example I posted above, if foobarfaz has to pass
> parameters as inputs
>> to foo and baz, then any changes to the parameters in foo
> won't affect baz
>> unless they are returned from foo and the modified
> parameters passed into
>> baz.
>>
>
> Steve, do you suggest to use only input / output args as a
> way of communication between functions? This may present
> another problem, but first I want to make sure I don't
> misunderstand you.
I try to do this where possible | | |