I would like a function (for example, a fit function) to return an anonymous function (usually stored in a struct
) that I can save and use later. However, passing @func
tends to pass a function pointer rather than the function itself. Is an inline
function the only way to do this? I would like to avoid inline
because it is extremely slow.
If that question is not clear, here is a sample of problematic code: I write a testFunc.m
file in some PATH
%testFunc.m
function myfunc = testFunc()
myfunc = @(x) x.^2;
end
I then store the function in a struct
. (I know this really should be an object!)
>> mystruct = struct;
>> mystruct.func = testFunc()
>> mstruct.x = [1 2 3];
>> save('myfile.mat','mystruct')
>> mystruct.func(mystruct.x)
ans =
1 4 9
If I then move myfile.mat
or testFunc.m
and load myfile.mat
, I cannot load the old struct. Instead, I get the error:
>> cd 'otherdir'
>> load('../myfile.mat')
Warning: Could not find appropriate function on path
loading function handle PATH/testFunc.m>@(x)x.^2
I know there is a problem because, if I check functions
>> functions(mystruct.func)
ans =
function: '@(x)x.^2'
type: 'anonymous'
file: 'PATH/testFunc.m'
workspace: {2x1 cell}
Is there some way to strip off the file workspace information? Are inline
functions the only solution?
If the functions you want to be anonymous are limited to being defined just in terms of their input parameters (like inline
functions are), and you can commit to keeping one function on your path, then you can make "sanitized" anonymous functions.
function out = sanitized_anon_fcn(str)
out = eval(str);
end
So, in your code, where you want to make an anonymous function, do this.
%testFunc2.m
function myfunc = testFunc2()
myfunc = sanitized_anon_fcn('@(x) x.^2');
end
As long as sanitized_anon_fcn.m stays on your path, you can delete testFunc2, and the saved function will continue to work. No special processing needed on save or load. Sanitized_anon_fcn
basically works like inline
but produces functions that are as fast as anonymous functions (because they are anonymous functions). The speed difference is about 10x in R2011b on my computer.
In the general case, where the functions might actually use variables from their workspace, things get trickier.
Caveat: This is a bit of a sick hack, and I do not endorse its use in production code. But as an example of how the language works, I can't resist posting it.
I think you're 90% there already. But you need to preserve the workspace info instead of stripping it off, because it may contribute to the operation of the function. Instead of saving the anonymous function handle, grab the output of that functions()
call you're making and save that.
fcn = testFunc();
fcn_info = functions(fcn);
save willbreak.mat fcn
save blah.mat fcn_info
Then load it back. You'll still get the same warning, but now the warning applies only to function handles captured inside the workspace of your top-level anonymous function. If your function doesn't actually reference them (and it shouldn't), you can ignore the warning and it'll work.
s0 = load('willbreak.mat') % will warn and return unusable function
warning off MATLAB:dispatcher:UnresolvedFunctionHandle
s = load('blah.mat') % will warn, but the first-level function will be usable
warning on MATLAB:dispatcher:UnresolvedFunctionHandle
Then pass it to something like this function which will bring your anonymous function back from the dead in a new workspace with the same workspace values, more or less.
function out = reconstruct_anon_fcn(s)
for iWks = 1:numel(s.workspace)
wkspace = s.workspace{iWks};
varnames = fieldnames(wkspace);
for i = 1:numel(varnames)
tmp = wkspace.(varnames{i});
eval([varnames{i} ' = tmp;']);
end
end
fcn_str = s.function;
fcn = eval(fcn_str);
out = fcn;
end
In our example case:
fcn = reconstruct_anon_fcn(s.fcn_info)
fcn(2) % and it works!
Now, all the loaded anonymous functions will claim to be from this new file, but it shouldn't matter, because it's just the snapshotted state of the workspace, not closed-over variables, that is used by anonymous functions. And in the case where there were anonymous function handles in the workspace that actually were used by the computation, you'll get an appropriate error saying "Undefined function handle".
This is a hack, but maybe you could take this and extend it in to something reasonably robust.
It is not possible, as it violates the concept of closure. Imagine that you define your anonymous function in that way:
function f = LocalFunc()
y = 3;
f = @(x)(x+y);
end
How can someone know about the fact that y
was 3
, besides saving the workspace info?
Edit(1)
One might say that you can capture the value
of the variable. But that is not always true. Imagine the following situation - You have a handle class, which has a method that adds to the input some property.
classdef Foo < handle
properties
DX;
end
methods
function y = AddDX(this,x)
y = x+ this.DX;
end
end
end
Now you create a function that creates anonymous function that calls the method:
function fOut = TestConcept(handleClass)
handleClass.DX = 3;
fOut = @(x)(handleClass.AddDX(x));
end
Now you create a new Foo() , and pass it to the TestConcept
func:
handleClass = Foo();
fOut = TestConcept(handleClass);
handleClass.DX = 3;
fOut(4)
The result is 7
And then change the handle, and call it again:
handleClass.DX = 100;
fOut(4)
The result is 104
. handleClass
value could not have been saved, since it is not a value type, it is a handle.
As you can see, you cannot always capture the values, sometimes you have a reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With