What is the recommended way to pass long list of parameters between main workspace and function?

27 views (last 30 days)
In my work I encountered a problem with passing variables betwen main script and function. Of course I can do it manually:
a1=1;a2=2;a3=3;a4=4;a5=5; %The list is much longer in my project, here just example
sum=calcvalues(a1,a2,a3,a4,a5);
function sum=calcvalues(a1,a2,a3,a4,a5)
sum=0;
b=[1,2,3];
for i=1:3
sum_tmp=sum_support(a1,a2,a3,a4,a5,b(i));
sum=sum+sum_tmp;
end
end
function sum_support = sum_support(a1,a2,a3,a4,a5,b)
sum_support=a1*a2+a3^b+a4*a5;
end
But I have to retype all values every time, and after any modification in list of function inputs there is no option to sync with other executions. Additionally, the list is very long, and take space in every function, making the code less readable. My intention was to override this with
function sum=calcvalues(varargin)
But this doesn't preserve file names. The output is just a 1x5 cell with anonymouse numbers. So far I managed to use eval to assign names dyanmicaly but this does not looks like a good practise. What is the recommended way to restore/preserve the names?
EDIT to clarify:
Here is the closest solution to what I expected. However this requires to use save command to unpack the structure. Is it possible to do this in more elegant way?
a1=1;a2=2;a3=3;a4=4;a5=5; %The list is much longer in my project, here just example
value_holder=struct('a1',a1,'a2',a2,'a3',a3,'a4',a4,'a5',a5);
sum=calcvalues(value_holder)
function sum=calcvalues(value_holder)
sum=0;
b=[1,2,3];
for i=1:3
sum_tmp=sum_support(value_holder,b(i));
sum=sum+sum_tmp;
end
end
function sum_support = sum_support(value_holder,b)
save('temp.mat','-struct','value_holder')
load('temp.mat')
sum_support=a1*a2+a3^b+a4*a5;
end
  7 Comments
Stephen23
Stephen23 on 24 May 2022
Edited: Stephen23 on 24 May 2022
"Does any of ODE solvers support nested function?"
There is nothing preventing you from using nested functions as the objective function in an ODE solver.

Sign in to comment.

Accepted Answer

dpb
dpb on 24 May 2022
If the variables are such as your example of a sequence of number variables with same root name, that implies they're related/same thing, just different instances. In that case I'd use an array and indexing -- "A" instead.
Alternatively, if they are actually different variables, take those that are related and create a structure to pass for succinctness and then reference the component variables in the struct.
Alternatively, if there are a lot of disparate variables, consider putting them into a table and pass the table.
Would need a lot more information about the actual variables and their use to have specifics, but design the data structure around the problem and then implement it, don't just create variables willy-nilly with no overall structure.
  10 Comments
Karol P.
Karol P. on 24 May 2022
Ok, after using profiler I must say that your solution is better. Maybe I will have to add extra few lines, but load/save tasks consume like half of the total operation time, so the choose is quite oblivious. I was not expecting they are so much slower than working inside workspace. Thank you!
Matt J
Matt J on 24 May 2022
Edited: Matt J on 24 May 2022
Alright, I'm mentioning this against my better judgement, but you can unpack the variables in the implicit manner that you want by doing,
eval(structvars(s)')
This might be a compromise in performance between load/save and my suggestion. However, eval() has at least the same programming hazards as load(), so take great heed of the discussion here,

Sign in to comment.

More Answers (3)

Steven Lord
Steven Lord on 24 May 2022
If all your variables are related and so should remain together, I would pack them into a struct array.
s = struct('a1',1,'a2',2)
s = struct with fields:
a1: 1 a2: 2
s.a3 = 3;
s.a4 = 4;
s.a5 = 5
s = struct with fields:
a1: 1 a2: 2 a3: 3 a4: 4 a5: 5
Now you can pass s into and out of your function and use the values in its fields in your function. Functions that need some but not all of the fields in the struct can ignore the ones they don't need, and you can add fields to the struct without affecting existing code.
  3 Comments
Steven Lord
Steven Lord on 24 May 2022
Unpack just the variables that ODE function needs at the top of the ODE function. Or if a variable only appears once in the ODE function, you can index into the struct rather than creating an additional variable.
s = struct('a1',1,'a2',2,'a3',3,'a4',4, 'a5', 5);
function sum=calcvalues(s)
sum=0;
You shouldn't name your variable sum. That identifier already has a meaning in MATLAB.
b=[1,2,3];
for i=1:3
sum_tmp=sum_support(s,b(i));
sum=sum+sum_tmp;
end
end
function sum_support = sum_support(s,b)
% The next few lines were added
a1 = s.a1;
a2 = s.a2;
a3 = s.a3;
a4 = s.a4;
% a5 = s.a5; % Instead of doing this you can use s.a5
sum_support=a1*a2+a3^b+a4*s.a5; % like I did here
end
dpb
dpb on 25 May 2022
Edited: dpb on 25 May 2022
For something that looks likes OP's example of a bunch of constants to retrieve, I think I'd do as you suggest and retrieve in the individual ODE functions the ones needed for the function --
s=struct('coeff',[1:5]); % put all the coefficients in array instead of many (unuseful anyway) names
Then in each ODE function can write something like
IDX=[1,3,4]; % the needed coefficients for this ODE
S=sum_support(s,IDX); % call the function with needed index array
function sum_support = sum_support(s,b)
sum_support=sum(s.coeff(b))
end
At command line to show syntax works as expected...
>> s = struct('coeff',[1:5]);
>> IDX=[1 3];
>> sum(s.coeff(IDX))
ans =
4.00
>>
One could make things more simple for using for the ODE functions by creating either a struct array of an element of the coefficients for the given ODE and even have a name to ensure use right array element or if it's not too complicated, use a 2D array where each row contains the coefficents for the given ODE so the very long 1D string is given some structure associated with its use.
The disadvantage is that they are then position dependent so Steven's solution is better in that regards.

Sign in to comment.


Matt J
Matt J on 24 May 2022
Edited: Matt J on 24 May 2022
In the case of your posted example, the appropriate re-implementation would be,
a=1:5;
result=calcvalues(a);
function accum=calcvalues(a)
accum=0;
b=[1,2,3];
for i=1:3
accum=accum+sum_support(a,b(i));
end
end
function sum_support = sum_support(a,b)
sum_support=a(1)*a(2)+a(3)^b+a(4)*a(5);
end
  6 Comments
Matt J
Matt J on 24 May 2022
Edited: Matt J on 24 May 2022
I said "if" they are, not that I know that for a fact. If they aren't the same length, you can still possibly use a cell array.
The point though is that you should go through your variables to see which ones are similar in size and type, and which can be grouped into arrays.

Sign in to comment.


Catalytic
Catalytic on 24 May 2022
Edited: Catalytic on 24 May 2022
Do not use load/save to introduce undeclared variables in your workspace. This can have unexpected effects as described in this doc page -
which also mentions recommended alternatives.
  5 Comments
Catalytic
Catalytic on 24 May 2022
The fact that you have a million different variables in your calculation means that the code will already be unreadable. Like the other contributors, I wonder if your variables could be better aggregated into vectors, matrices, and other array types. The MAT in MATLAB stands for matrix, and you should be using them.
Karol P.
Karol P. on 24 May 2022
The project is very complicated, and no, the values cannot be grouped like you suggest. If it would be that simple I would not bother to ask. Anyway, thank you for your suggestions, in more generalised case, you're right of course.

Sign in to comment.

Products


Release

R2022a

Community Treasure Hunt

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

Start Hunting!