How to save a function handle to a .m file

49 views (last 30 days)
If I create a function via inline, e.g.
myfunc = @(x,y,z) x.^2+y+z
How can I save this as a matlab file function? I can do this if I chose to make a symbolic function this way
syms x y z
myfunc(x,y,z) = x.^2 + y + z;
matlabFunction(myfunc,"File","myfunc")
But I would like to accomplish this without having to create a symbolic function.

Accepted Answer

Rik
Rik on 23 Apr 2023
I forgot the name of function, but a few years ago, a function was introduced to capture the output to the command window to a variable. There was a blog post about it, so you could search there.
You can use that to capture the function and the inputs. Then you can write that to an m file if you wish.
The trouble is that an anonymous function will capture variables if it needs them, so you will have to find a way to recreate those variables as well.
Is that what you mean?
  6 Comments
Walter Roberson
Walter Roberson on 24 Apr 2023
But this doesn't allow me to feed the function inputs
You need to decide: are you asking for a .m file that
  • is a script that assigns an anonymous function to a known variable name?
  • is a function that returns the handle to an anonmous function that can be used in place of the previous anonymous function?
  • is a function that can be directly passed inputs and returns the same values that would be calculated by the original anonymous function?
Is it permissible to "cheat" by having code that saves the anonymous function to a .mat file and reloads it when you want it back? Is the idea that you want to end up with something that you can execute on a parallel worker and it will work "properly" including all captured variables? Including handling captured global variables? Or are you looking to write the resulting file content to a text field in a database?
How should shared access to persistent variables be handled?
Rik
Rik on 24 Apr 2023
Edited: Rik on 24 Apr 2023
You still need to parse the text to a normal function:
my_fancy_function = @(x,y,z) x.^2+y+z;
write_anon_function_to_file(my_fancy_function)
dbtype my_fancy_function.m
1 function out = my_fancy_function(x,y,z) 2 out = x.^2+y+z; 3 end
The name will be automatically detected, but if that doesn't work, it will revert to the name myfunc.
write_anon_function_to_file(@(x)x+2)
dbtype myfunc.m
1 function out = myfunc(x) 2 out = x+2; 3 end
function write_anon_function_to_file(func)
% This will write an anonymous function a an m file.
% Note that this will only work if the function does not have capture
% variables. So this will not work:
% a = 100;
% func = @(x) x+a;
% write_anon_function_to_file(func)
% But this will:
% func = @(x) x+100;
% write_anon_function_to_file(func)
% Determine the function name.
try
name = inputname(1);
if isempty(name),error('trigger catch block'),end
catch
name = 'myfunc';
end
% Extract and parse the function contents.
txt = formattedDisplayText(func); % capture the output
txt = char(txt); % convert to char
txt = strtrim(txt); % remove leading and trailing whitespace
ind = strfind(txt,')'); ind = ind(1); % find the end of the input arguments
% Compose the function and write to file.
txt = sprintf('function out = %s(%s)\nout = %s;\nend', ...
name,txt(3:(ind-1)),txt((ind+1):end));
writelines(txt,[name '.m']);
end

Sign in to comment.

More Answers (1)

Walter Roberson
Walter Roberson on 24 Apr 2023
myfunc = @(x,y,z) x.^2+y+z;
writelines("myfunc = " + func2str(myfunc), "myfunc.m")
%crosscheck
dbtype myfunc.m
1 myfunc = @(x,y,z)x.^2+y+z
  2 Comments
Walter Roberson
Walter Roberson on 24 Apr 2023
myfunc = @(x,y,z) x.^2+y+z;
namedfunc2file(myfunc)
%crosscheck
dbtype myfunc.m
1 myfunc = @(x,y,z)x.^2+y+z
function namedfunc2file(funhandle)
if nargin < 1; error('must provide named function handle'); end
funname = inputname(1);
assert(isa(funhandle, 'function_handle') && ~isempty(funname), 'must be function handle assigned to variable');
funs = func2str(funhandle);
writelines(funname + " = " + funs, funname + ".m");
end
This could be improved to test whether the function handle is anonymous or not using functions() . If it is not anonymous then the file field returned by functions() will be empty for built-in functions, and otherwise will indicate the file that contains the function definition. If functions() says the type is simple then either it is the handle to a built-in function (in which case file field will be empty) or else it is the handle to a function that has its own file. If functions() says the type is scopedfunction then the handle is to a function defined within the file for a script, or is defined after the initial function for a function file. If functions() says the type is "nested" then the handle is to a function defined inside another function.
The case of simple and non-empty file field would seem at first to be the easiest to deal with -- but remember the goal is to extract only the function so named, and there might be additional functions after it in the file (some of which might be essential for the function to operate)....
The other cases other than 'simple' with empty file would require locating the function definition inside the containing file.
Walter Roberson
Walter Roberson on 24 Apr 2023
Okay, getting back to anonynmous functions:
z = rand(1,3)
z = 1×3
0.6491 0.8028 0.0075
myfunc = @(x,y) x.^2+y+z;
S = func2str(myfunc)
S = '@(x,y)x.^2+y+z'
Notice that the text contains the name z, not the value associated with z inside the anonymous function:
info = functions(myfunc)
info = struct with fields:
function: '@(x,y)x.^2+y+z' type: 'anonymous' file: '/tmp/Editor_gxmvy/LiveEditorEvaluationHelperEeditorId.m' workspace: {[1×1 struct]} within_file_path: ''
info.workspace{1}.z
ans = 1×3
0.6491 0.8028 0.0075
so if the idea is that the generated function file should replicate the behaviour of the anonymous function, then really you need to examine the workspace and have code that assigns constant values to those variables. But... keep in mind that captured variables might themselves be anonymous functions. and that the captured anonymous functions might be variables with the same name
g = 2;
g = @(x) g * ones(size(x))
g = function_handle with value:
@(x)g*ones(size(x))
g = @(x) g(x) .* (1-g(x));
info = functions(g)
info = struct with fields:
function: '@(x)g(x).*(1-g(x))' type: 'anonymous' file: '/tmp/Editor_gxmvy/LiveEditorEvaluationHelperEeditorId.m' workspace: {[1×1 struct]} within_file_path: ''
inner_g = info.workspace{1}.g
inner_g = function_handle with value:
@(x)g*ones(size(x))
inner_info = functions(inner_g)
inner_info = struct with fields:
function: '@(x)g*ones(size(x))' type: 'anonymous' file: '/tmp/Editor_gxmvy/LiveEditorEvaluationHelperEeditorId.m' workspace: {[1×1 struct]} within_file_path: ''
inner_info.workspace{1}.g
ans = 2
so we cannot simply (reliably) recurse and generate functions with the same name of nested anonymous functions: we would have to generate new names for each nested anonymous function and rewrite the function body to use the name name... and hope it wasn't sensitive to exact variable names.

Sign in to comment.

Categories

Find more on Function Creation in Help Center and File Exchange

Products


Release

R2022a

Community Treasure Hunt

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

Start Hunting!