Replacing variable names when using func2str of anonymous function

I have an anonymous function, say
f=@(x,y) x+y;
I can name the function using func2str:
func2str(f)
ans =
'@(x,y)x+y'
What if I want to get the function name with different names for the anonymous variables x,y. Say replacing x,y with the strings 'Small', 'Large' to yield:
'@(Small,Large)Small+Large'
This becomes more tricky if the function has x or y also as non-variables. say for the case of f(x) x+'x', the correct answer will be '@(Small)Small+'x''

15 Comments

And also, why would you want to do this? What is your end goal?
How to do it? How to get a string representing the function name but when renaming the function variables
Rik's question is important. I'm certain a regular expression could do what you're asking but it sounds like maybe you're not heading in a useful direction since the variable names shouldn't matter.
The goal is simply to get a human-sensible string representation of the function name.
If you have the Symbolic Toolbox, then in many (but not all) cases the easiest approach is to call the function passing in the new names you want to be used, possibly followed by a matlabFunction() call to turn it back into an anonymous function.
There are some things that you cannot use this for:
  • mod() applied to a symbolic expression will give you answers you do not expect
  • If there are logical sub-expressions to implement piecewise or dirac delta operators, such as 5*(x<10) + 15*(x>=10) then you will not get the answer you want
  • calls to numeric integration functions will give problems
  • calls to interp*() or conv*() will give problems
  • I know there are others that are not coming to mind right at the moment
The goal is simply to get a human-sensible string representation of the function name.
If you, the author of the code that's trying to convert the function handle into text, created the function handle the right place to make the function handle readable would seem to be when you create it.
If you took text input from the user and converted it into a function handle, how did you determine what variable names the user used in their text input to represent the inputs to the function handle? Can you adapt that parsing you've already done to identify uses of the input arguments and replace them?
If your user created the function handle and passed it into your code, ideally either the user can understand the variable names they used when they defined (in which case it's already readable) or they can't (in which case, why did they choose those variable names?)
But I suspect there's some context of which we're unaware that may make this a more constrained, more interesting and/or more challenging problem. Can you share that context (how the function handle gets created, how the variable names used as inputs and outputs are selected, why you're converting the function handle to text) with us?
Thansk much Steve. The user is providing a function handle. The variables transfered ot the function has names that are not known at the time of creation of the function.
I still think a well crafted regular expression can 1) identify the inputs following "@", bounded by parentheses and separated by commas and 2) replace those input names by identifying complete matches that aren't bounded by single or double quotes.
Walter's symbolic idea is another avenue worth pursuing.
While a well crafted regular expression would probably work most of the times, I fear that there may be some cases where it would fail, renaming a non-variable part of the string which matches a variable name. On the other hand anonymous functions are fairly limited in what you can write in them, so maybe not.
But ultimately, the only guaranteed way to do this reliably is to parse the function code. The symbolic toolbox would probably do that for you as Walter pointed out. The undocumented mtree would certainly do that but good luck figuring out how to use it. Plus, I understand that it's not using the true matlab parser. Or you'd have to write your own parser. Not an easy task!
If a user chooses to provide the function handle @(x, y) x+y, are you concerned that they will be confused / annoyed that you show them the function handle @(small, large) small+large? [For such simple function handles, maybe not; for more complicated ones, maybe.]
There have been posts here where people have complained that Symbolic Math Toolbox displayed the expression they entered in a different order, like displaying y+x as x+y. See this post, this one, this one, etc. as examples. So this isn't an academic concern.
I am not concerned about the order, x+y or y+x
One thing to note about the Symbolic Toolbox route is that it will simplify expressions and you cannot stop it from doing so. For example,
syms x y
2 + x + y + 5 + x + 2*y
then there is no (realistically usable) way to prevent that from being rewritten as
2*x + 3*y + 7
In some cases this does not matter at all, and can be a good thing, but in other cases it can be important, as it can change the numeric round-off in ways that might not be acceptable.
The reordering issue can be catastrophic if you have something like y*x and it gets rewritten as x*y ... and you will be using it for matrix multiply or quaternion multiply which are non-commutative.
James has a good point. For use with the symbolic Toolbox, all of the variables need to represent scalar values. The work-around for that is to pass in a matrix of symbolic variables such as
sym('x', [4 4])
for a variable that is expected to be 4 x 4.
Unfortunately, picking back through the results of the expression to repackage the individual variables into an array can be a nuisance.

Sign in to comment.

 Accepted Answer

As I suggested, it's possible to do this with the undocumented mtree function:
function fstr = replacevar(fstr, vars, replacements)
%fstr: the code of an anomymous function (obtained with str2func). This code is only designed to work with anonymous functions.
%vars: a cell array of variable names to replace
%replacements: a cell array of replacement names
tree = mtree(fstr); %parse the function and get parse tree. Note that mtree is completely undocumented
fvars = tree.strings; %portion of the function that correspond to each parsed element. Includes variables and non-variables for now
[isrep, whichrep] = ismember(tree.strings, vars); %do any of the potential variable match one to be replace and which one?
toreplace = strcmp(tree.kinds, 'ANONID') & isrep(:); %replace elements that are ANONID (input variables of the anonymous function) and match a replaced variables
fvars = fvars(toreplace); %filter the list of potential variables to keep only the ones to be replaced
whichrep = replacements(whichrep(toreplace)); %get matching replacements
varloc = tree.position; %start position of variables in the strings (not sure it's the correct property but appears to be right)
varlength = cellfun(@numel, fvars); %couldn't find a length property in tree. get it from fvars instead
splits = [varloc(toreplace)'; varloc(toreplace)' + varlength]; %compute where to split fstr to isolate the variables
fsplit = mat2cell(fstr, 1, diff([1; splits(:); numel(fstr)+1])); %do the splitting every even element is a variable to replace
fsplit(2:2:end) = whichrep; %do the replacing
fstr = [fsplit{:}]; %and merge back
end
Of course, since mtree is completely undocumented, it's possible it breaks on some inputs and may stops working in future versions. Use at your own risk, but it appears to do the job correctly:
>> replacevar('f = @(x, yyz) strrep(x, ''x'', yyz)', {'x', 'yyz'}, {'str', 'reps'})
ans =
'f = @(str, reps) strrep(str, 'x', reps)'

2 Comments

Wow!
Genius. Works like magic.
Note that if the anonymous function contains another anonymous function definition using the same variable names, e.g.:
f = @(x, y) arrayfun(@(x, y) x+y, x, y);
Then the variables currently get replaced in both function. The code could be improved to not do this. This is left as an exercise to the reader, one that has time to work out how to get the information out of mtree. I believe it's probably not too complicated but I haven't got the time to work it out.

Sign in to comment.

More Answers (0)

Products

Release

R2019b

Asked:

on 24 Sep 2019

Commented:

on 27 Sep 2019

Community Treasure Hunt

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

Start Hunting!