Structures with multiple, conntected anonymous functions

I have a rather complicated workflow, however, I am having trouble identifying a way for it to work.
I start with a json of the form:
{
"x":3,
"y":6,
"fx":"@(a) a^2+y",
"fy":"@(a,b) y*fy(b)+b*x"
}
This gets read in and evaluated such that:
S.x = 3
S.y = 6
S.fx = @(a) a^2+y
S.fy = @(a,b) y*f(b)+b*x
I have tried both eval and str2funct to create the anonymous function.
Later in the workflow, this structure gets packaged into another structure
dS.S = S;
This is to help with passing a model around between functions.
The problem is, that later on, this command will give an error:
dS.S.fy(1,1)
The error states that the function fx cannot be found. However,
dS.S.fx(1)
does work, WITHOUT there being an "y" defined in the workspace.
Can anyone shed some light on this?

3 Comments

Are the x and y in your function handles supposed to pick of S.x and S.y at the time of evaluation?
What is f(b) in this line
y*f(b)+b*x
is it supposed to be fx(b)?
@James Tursa, they do not need to pick up S.x or S.y. These values will not change once they are read in.
@Ameer Hamza, yes that is a typo

Sign in to comment.

 Accepted Answer

Ok. I know "eval is evil" but here it makes everything quite simple in this case. You can define a helper function like this
function s = helperFun(s)
names = fieldnames(s);
for i=1:numel(names)
eval(sprintf('%s=s.%s;', names{i}, names{i}));
end
for i=1:numel(names)
if ischar(s.(names{i}))
s.(names{i}) = eval(s.(names{i}));
end
end
end
and then call it on your json file like this
S = jsondecode(fileread('test.txt'));
S = helperFun(S)
ds.S = S;
Then call the functions
>> ds.S.fx(1)
ans =
7
>> ds.S.fy(1, 2)
ans =
246

2 Comments

This is exactly what I have done. Thank you. I have marked this at the accepted answer for future readers.

Sign in to comment.

More Answers (1)

Are you sure that y didn't exist in the workspace when you defined the structures? Because in a fresh workspace,
S.x = 3
S.y = 6
S.fx = @(a) a^2+y
S.fy = @(a,b) y*f(b)+b*x
dS.S = S
dS.S.fx(1)
gives the error
Unrecognized function or variable 'y'.
Error in @(a)a^2+y
Does that shed light?

9 Comments

Sorry, I left this detail out. When reading the json file into the structure, are also create the varaiables in the function workspace, just before the anonymous function is created.
When replicating the example, it looks like this:
x =3;
y = 6;
S.x = 3
S.y = 6
S.fx = @(a) a^2+y
S.fy = @(a,b) y*fx(b)+b*x
dS.S = S
clearvars x y
dS.S.fx(1)
dS.S.fy(1,1) % causes error
The error is because there is no fx function. There is an S.fx function, and a dS.S.fx function, but there is no fx function. When you call dS.S.fy and it goes looking for fx ... it isn't there.
You need to build S differently so that S.fy actually points to the fx function handle you want it to (S.fx). Currently the fx in S.fy is not pointing to anything, so when it is invoked it looks for such a variable or function in the workspace etc.
E.g., you could build the function handles in the workspace first and then attach them to the struct S.
Yes. I find it interesting that dS.S.fx(1) works while pointing to a "y" variable that is no longer defined. And hence the question, what is a better way to do this? A class?
Agreed that it's interesting. Given James' reasoning, I might have expected this analogous statement to be true:
"There is an S.y variable, and a dS.S.y variable, but there is no y variable. When you call dS.S.fx and it goes looking for y ... it isn't there."
If you used the debugger to step into dS.S.fy before it evaluates, you'll see the workspace of the anonymous function looks like this:
So, y is there, but fx is not. I frankly don't fully understand it, but my guess is that this is because S.y can be evaluated, but S.fx cannot be (without further input).
The closest I could get to the spirit of what you were trying to do is
x = 3;
y = 6;
S.x = 3;
S.y = 6;
S.fx = @(a) a^2+y;
S.fy = @(a,b,fx) y*fx(b)+b*x;
dS.S = S;
dS.S.fx(1)
dS.S.fy(1,1,dS.S.fx)
That is interesting. Before all of this I did not know that anonymous functions had a workspace.
It turns out I am going to have to do something different all together, as I have yet another anonymous function in a different structure array that is read in from a different file, that references variables from the first.
Function handles take snapshots of everything in the workspace that is part of them at the time they are built. It doesn't matter if you subsequently change those variables or even clear them downstream in your code because the function handle has stored inside of it those snapshots. The only way to change those snapshots is to rebuild the function handle from scratch. E.g.,
>> a = 4;
>> b = 5;
>> fx = @(x)a*x+b; %<-- fx stores snapshots of a and b inside itself
>> fx(2)
ans =
13
>> a = 6; % <-- this changes a, but does not change the snapshot of a inside fx
>> b = 7; % <-- this changes b, but does not change the snapshot of b inside fx
>> fx(2)
ans =
13
And then suppose we create a new function handle fy:
>> fy = @(x,y) fx(x)+a % <-- fy stores current snapshots of fx and a
fy =
function_handle with value:
@(x,y)fx(x)+a
>> fy(2,3)
ans =
19
Now change fx:
>> fx = @(x)b*x+a % <-- fx stores current snapshots of a and b
fx =
function_handle with value:
@(x)b*x+a
>> fy(2,3)
ans =
19
Notice that fy didn't change. That is because fy is working with it's shapshot of fx as fx existed at the time that fy was created. It doesn't matter that you subsequently changed fx downstream in your code.
You could do this to get fx defined properly in fy:
x =3;
y = 6;
S.x = x
S.y = y
fx = @(a) a^2+y % <-- fx stores shapshot of y
fy = @(a,b) y*fx(b)+b*x % <-- fy stores snapshots of fx and x and y
S.fx = fx
S.fy = fy
BOTTOM LINE: Function handles always work with shapshots of workspace variables at the time of their creation. If you want different behavior, use actual function files that will go looking for functions in realtime instead of using function handles.
This is what worked best for me. I believe it is essentially what James way saying.
I loop through that JSON file once and put all of those variables and functions into the workspace as the appropiate variable or function.
I loop through the variables and functions again and then assign them into a structure "S".
This structure is passed out of the reading function and into the main script, which assembles it into a larger structure "dS", which get's passed in to a few different functions for calculation. This seems to work.
Caveats:
  • The variables used by the anonymous function that are not input variables cannot be updated
  • The other functions reference by the anonymous functions must exist in the workspace when they are created.
Thanks all for you help. I will go ahead and mark it as resolved.
Yes, it appears you understand how function handles work now. Your current approach seems correct to me.

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!