Equivalent of function handle for scripts?

12 views (last 30 days)
Dear all,
I am currently working in a project for which I would like to create an equivalent of function handles for scripts. To be more precise: I have a code at the beginning of which the user sets a few options before an algorithm with a large number of repetitions runs. Depending on the options chosen at the beginning of the code, one script will be selected among several (say for instance among script1 and script2), and this script will then be run at every repetition of the algorithm.
So of course one option is to use an "if" statement within the algorithm: at every iteration the "if" statement selects the desired script and executes it. But I am afraid that the "if" statement repeated at every iteration might slow down the code (I may have a few millions of repetitions). Ideally, I would like to create some command that assign a handle to the correct script at the beginning of the code, before the algorithm starts. For instance, if the options select script 1, assign myscript=@script1. If conversely the options select script2, then assign myscript=@script2. In both cases, the algorithm only has to call for myscript, without further if statement.
I tried to use the handle @ on my scripts but it fails: handles defined this way seem to be only calling for functions. It seems also that replacing my scripts with functions to use regular handles is not an option: the scripts (turned into functions) would call for different arguments, and different numbers of arguments. So I would again have to use "if" statements at each iteration to vary the list of arguments. I cannot either use functions without inputs nor outputs since the commands implemented by the scripts have to update variables in the global space.
So at the moment I am stuck. I did not imagine that I would struggle with something as simple as assigning a handle to a script, but it seems that Matlab really can't do that. Anyone has a solution for this? Or is the "if" statement the best solution in the end?
Thanks a lot
edit: simple worked example
% script1.m
x=x+y*z;
% script2.m
x=x-w;
% code.m
x=1;
% options: choice of script (1 for script1, 2 for script2)
choice=1;
% variables generated by the choice
if choice==1
y=1;
z=2;
elseif choice==2
w=0.5;
end
% algorithm
for i=1:10000000
% if statement
if choice==1
script1
elseif choice==2
script2
end
end
Ideally replace with:
% script1.m
x=x+y*z;
% script2.m
x=x-w;
% code.m
x=1;
% options: choice of script (1 for script1, 2 for script2)
choice=1;
% variables generated by the choice
if choice==1
y=1;
z=2;
myscript=@script1
elseif choice==2
w=0.5;
myscript=@script2
end
% algorithm
for i=1:10000000
myscript
end

Accepted Answer

John D'Errico
John D'Errico on 1 May 2018
Edited: John D'Errico on 1 May 2018
No script handle needed at all.
listOfScriptNames = {'script1', 'script2', 'otherboringscript', 'myreallyinterestingscript'};
Now, when you use this, you will have some script chosen, as a number, from 1:4.
So lets say that you have somewhere in your code identified the number of that script as:
scriptNumber = 4;
It may have come from a menu choice, thus a callback, or whatever. You can use that script as easily as this:
eval(listOfScriptNames{scriptNumber})
While I try mightily to avoid use of eval, it is not the end of the world when you are forced to use it on a limited basis.
Ok, do you really, absolutely insist on creating this as a function handle? So something you can pass around at will? Sigh. Ok, I found a way to do it. You need to use str2func. Try this:
I've created a script on my search path with the name myreallyinterestingscript.m. You can see it here:
>> type myreallyinterestingscript.m
% My really interesting script
disp('Help! The sky is falling!')
Ok, not that interesting. In fact, boring as it can be. But it is 5 am here, and I'm not feeling that creative.
scriptnumber = 4;
f = str2func(listOfScriptNames{scriptNumber});
Now, can I use it as a function handle? No problem.
>> f()
Help! The sky is falling!
>> f()
Help! The sky is falling!
>> f()
Help! The sky is falling!
I can pass f around as the argument to a function, anything at all. Just evaluate it as f().
If you try to just use it without the parens,
f
f =
function_handle with value:
@myreallyinterestingscript
it just dumps the contents of the function handle to the command window. So remember to use it as f().
  3 Comments
Stephen23
Stephen23 on 2 May 2018
Edited: Stephen23 on 2 May 2018
@John D'Errico: why not just use run, rather than the sledgehammer eval ?

Sign in to comment.

More Answers (1)

Steven Lord
Steven Lord on 1 May 2018
I'd like to suggest a different approach, one that somewhat insulates what the scripts compute from the infrastructure. Instead of passing an arbitrary number of arbitrary variables "into" and between the script files, store your data together in one struct array. Each of your scripts would then become a self-contained function (that you could test in isolation from the rest of the system, if you so desire) that accepts and returns that struct array. If your functions need to use local variables, you wouldn't need to worry about those local variable names conflicting with variable names you use in your infrastructure.
function S = function1(S)
S.x = S.x + (S.y * S.z);
function S = function2(S)
S.x = S.x - S.w;
% main code
S = struct();
S.x = 1;
S.y = 1;
S.z = 2;
S.w = 3;
% Instead of the previous 5 lines you could use
% S = struct('x', 1, 'y', 1, 'z', 2, 'w', 3);
choice = 1;
switch choice
case 1
ftc = @function1;
case 2
ftc = @function2;
end
for i=1:10000000
S = ftc(S);
end
If you want to add a function3 later on that needs to operate on x, z, and w all you would need to do is add the function definition and update the switch / case section.
If you wanted to add a function4 that operates on x, z, w, and a new variable q all you would need to do is add the function definition, add q to the struct S, and update the switch / case section.
There is a way using str2func to eliminate the need to update the switch / case section (or even the need to have the switch / case section) but I'll leave that as an exercise.
  3 Comments
Steven Lord
Steven Lord on 1 May 2018
I understand that there are often many different approaches you can use. I'd like to point out one mitigation strategy for your concern about long and complication expressions.
If I were writing a longer functions or one that contained more complicated expressions that included a number of the variables repeatedly, I could use the struct solely for transport in and out of the function. Inside the function I could "unpack" what I needed at the top of the function and "pack" it back into the struct at the end.
function S = function3(S)
% I plan to use S.x all over the place inside function3.
% So to make those expressions simpler, shorter, and easier to read
x = S.x;
% Do lots of stuff with x
% more stuff involving x ...
% even more stuff using x ...
% At the end
S.x = x;
Because function3 is a function, there's no risk of the local variable x conflicting with a variable named x used by your infrastructure.
Romain
Romain on 2 May 2018
Hi Steven,
thanks again for the interesting discussion. When I do have to use structures, I actually follow the unpacking strategy you suggest. It is, I think, the best thing to do. However, it adds a few lines (sometimes, many lines) of unpacking, then a few lines (possibly many) of re-packing. That also contributes to lengthen the code. So again, it is a matter of preferences, and sometimes of circumstances. For the code I am currently working with, a no-structure strategy was best. For some projects I had to work with structures, in this case the pack/unpack strategy is best.

Sign in to comment.

Categories

Find more on Variables 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!