Reputation: 52870
I have a file funcs.m that stores anonymous functions. They must be usable by the files in the directory where it is. Currently, I use the anonymous functions so that I execute the file funcs.m
in different files but I think this is a a wrong way of doing things. The other functions such as main.m and its nested function nest.m need to use the anonymous functions from funcs.m. I think paths won't solve this problem because the files are in the same folder. Basically I could solve this problem by copy-pasting the anonymous functions to every file but code-smell so:
Is there some way of reusing the funcs.m having the anon functions in Matlab?
Example
main.m
function main funcs; % loads the anonymous functions nest(par1,...,parN) end
nest.m
function nest(par1,...,parN) funcs; %ERRR: This fires err, why? Look: this was sourced already in main.m! function neededOnlyHere(par100) bq(q,A) %This needs the functions of the funcs end neededOnlyHere(somePar) %ERR to use the anon funcs from funcs end
Functions main.m and nest.m use this function funcs.m having the anonymous funcs
bq=@(q,A) q*A; %Bolded q I=@(ii,jj,A) find(A(ii,:)==1 & A(jj,:)==0); AiNotj=zeros(1,Ncut); ...
ERROR
Attempt to add "bq" to a static workspace. See MATLAB Programming, Restrictions on Assigning to Variables for details. Error in funcs (line 10) bq=@(q,A) q*A; %Bolded q
Upvotes: 2
Views: 2880
Reputation: 23908
You get the error when calling it in nest.m
because having a nested function makes its enclosing function's workspace a "static workspace"; that is, variable names cannot be added via eval()
, assignin()
, or other "dynamic" techniques; only variables that are explicitly assigned in that function's text are allowed. Evaluating a script to define local variables - which is what you're doing when calling funcs.m
- is "dynamic", so prohibited in functions with nested functions. It works in main.m
because main has no nested functions and is thus a "dynamic" workspace.
There are a couple ways you could change it to work with static workspaces and nested functions. The first thing to ask is whether you really need to make them anonymous functions?
If you don't need them to be anonymous functions per se, just break them out and put each one as a regular function in its own .m
file; e.g. bg.m
, I.m
, AiNotj.m
, and so on. Then they're all available to all other functions in that directory.
If that turns in to a mess of files, or if you want to scope them and maybe make them available only to the selected functions that really need them (that is, the functions currently calling funcs()
), then you can stick them in a package. Create a subdirectory called +myfuncs
and move all the little function files in there; e.g. +myfuncs/bq.m
, +myfuncs/I.m
, +myfuncs/AiNotj.m
. (The +
prefix tells Matlab the directory is a package.) Then you can pull all of them in to your function scope by doing import myfuncs.*
as a direct replacement for where you're currently calling funcs()
.
function nest(par1,...,parN)
import myfuncs.*;
function neededOnlyHere(par100)
bq(q,A) % This will work, resolving to myfuncs.bq
end
You can do the import myfuncs.*
from the command line to make them available interactively, too.
This is probably how Matlab itself wants you to organize clusters of related functions like this, and would be my first approach. It's the least "smelly" IMHO. If you really wanted to be able to edit them all in a single file like funcs.m
for convenience, you could write a little code munger in Perl or whatever that parsed funcs.m and output them all as equivalent individual functions as a preprocessing step. (I think it's a bit of a bummer that you can't define multiple top-level functions in an M-file like this, but oh well.)
If you really need to work with anonymous functions, there are some workarounds.
You can change your funcs() function to actually return a struct of all those anonymous functions, using field names instead of local variable names.
function out = funcs
out.bq=@(q,A) q*A; %Bolded q
out.I=@(ii,jj,A) find(A(ii,:)==1 & A(jj,:)==0);
out.AiNotj=zeros(1,Ncut);
For this, you'd have to prefix all the function references with the struct name you're holding them in. Don't know how big a deal this is for you.
function nest(par1,...,parN)
fs = funcs;
function neededOnlyHere(par100)
fs.bq(q,A) %This needs the functions of the funcs
end
To get funcs() to work as-is, you can statically pre-allocate variables with all the function names you're going to use, so the Matlab parser recognizes them as statically assigned variables. Then when you call funcs(), it will re-assign the values of the existing variables, which is permissible in dynamic workspaces.
function nest(par1,...,parN)
[bq, I, AiNotj] = deal(); % Preallocate all names from funcs
funcs;
function neededOnlyHere(par100)
bq(q,A) %This needs the functions of the funcs
end
This would be a bit of a pain, because you'd have to re-edit every file that uses funcs whenever a new function name is added. You could at least write a little perl script to auto-generate that line of code by parsing funcs.m
and outputting a "[bg, I, AiNotj,...] = deal();" with all the functions it finds, and you can just copy that in to your code.
Another way to do this would be to have funcs actually return all the functions in its output list. This would have the benefit of continuing to work even as you add new functions to funcs.m
, as long as you don't remove or change the order of your existing anonymous functions.
function [bg,I,AiNotj] = funcs()
bg = ...
I = ...
% And then in the calling functions:
[bg,I,AiNotj] = funcs(); % which you can copy and paste from funcs.m's header
Upvotes: 6
Reputation: 5359
There are many ways of passing anonymous functions:
1) Pass the function itself:
function main
f = @(t) t^2 - 3;
param = randn(12,1);
g = test22(param,f);
disp (g)
end
function g = test22(param,f)
g = f(param(2));
disp(param(2))
end
2) Use globals (which usually should be avoided in complex code)
function main
global f
f = @(t) t^2 - 3;
param = randn(12,1);
g = test22(param);
disp (g)
end
function g = test22(param)
global f
g = f(param(2));
disp(param(2))
end
Upvotes: 1