Best Practice: Many Functions relying on Global Variables?

60 visualizzazioni (ultimi 30 giorni)
TL;DR: Many answers and help pages recomend avoiding global variables. However, in the case of many functions relying on the same constants, it feels almost unavoidable. What then is best practice? Just use global variables, or is there something better?
The recomendations against global variables
  1. https://www.mathworks.com/help/matlab/ref/global.html
  2. https://www.mathworks.com/matlabcentral/answers/347418-global-variable-not-seen-by-function-the-function-creates-a-new-empty-variable
  3. https://www.mathworks.com/help/optim/ug/passing-extra-parameters.html
  4. https://www.mathworks.com/help/matlab/math/parameterizing-functions.html
The Conundrum
I want to explore how a system with many physical parameters varies in response to changes to some of those variables. Say I have ~50 parameters, and I want to sweep a couple of them at a time.
I'd like to break my code up into modular pieces that correspond to physical subsystems that I am simulating.
My Current Solution
I tucked all my parameters into a file, "loadParams.m", which contains over 50 variable assignments like: thing1 = 7.34983. I did not make it a function, so that all the assignments by default go into my main file workspace (which I'm not sure is the same as global or not)
To sweep a few of the parameters, in my main file I have:
loadParams; %this is a script (no not a function). This defines parameters 'thing1' through 'thing50'.
thing2 = 8; %changing one of the parameters from default
for thing1 = 1:20
procedure1 %computes and stores a results0 variable
result1 = myfun1(myfun2()+myfun3(), myfun4(7));
result2 = myfun5()
end
%more stuff follows to plot the results.
Is there a better way to do this?
Issues with solutions I've seen
  • Just plug all the variables as arguments to each function.
  • With 50 parameters, this would make the code bulky and unreadable
  • Use global variables like I did:
  • easy to make mistakes.
  • I've been having some issue with the 'loadParams' parameters not getting read by my functions.
  • Then I have to go in and pepper 'global' inside my various files.
  • Use annonomous functions
  • still ends up being bulky.
  • I'd have to define my functions with 50+ inputs
  • Then I'd have to clutter my main file with myf1 = @(variable) myfun1(thing1,thing2,thing3,thing4 ...)
  • And then I'd have to do that for each function, and hope that I type in the same values for thing1 ...
  • Use functions that assume default values for any variable not passed to the function
  • Matlab doesn't seem to have a clean way of doing this
  • I'd have to put in default values for each function that are the same across all of the functions, so
  • it's easy to make mistakes.
  • And hard to change the defaults
  9 Commenti
Bruno Luong
Bruno Luong il 15 Nov 2022
Modificato: Bruno Luong il 15 Nov 2022
I don't see why Steve method is complex.
The structure method does need to passing around as argument of functions.
Steve method is simpler: call it when one need it. In contrary it's the most simple and most efficient.
Stephen23
Stephen23 il 15 Nov 2022
Modificato: Stephen23 il 15 Nov 2022
"I don't see why Steve method is complex."
You are a highly experienced programmer with many years of experience. Not every one is at your level.
Some users struggle with indexing. Some users struggle with getting a single function defined or called.
"In contrary it's the most simple ..."
Perhaps nested functions are?
But really until we define metrics for "complexity" and "efficiency", this is all moot. Thanks for the interesting chat.

Accedi per commentare.

Risposta accettata

James Tursa
James Tursa il 14 Nov 2022
See this link for a related discussion involving the best way to define global constants, in particular Steven Lord's answer:
Basically create a class with properties and use that everywhere. The units of the properties are self documenting using this technique.
  6 Commenti
Steven Lord
Steven Lord il 15 Nov 2022
You can have Static methods of a class that can be called without creating an instance of that class. In fact this class could even be Abstract if you want to ensure that users can't instantiate an instance of it.
classdef (Abstract) example1850048
properties(Constant)
mph2kphFactor = 1.60934; % Thanks Google
end
methods(Static)
function kph = mph2kph(mph)
% mph2kph Converts miles per hour to kilometers per hour
kph = mph*example1850048.mph2kphFactor;
end
end
end
I can't run this here in Answers since we can't define classes in scripts, but if you copied that simple class into MATLAB and then ran this line of code you'd receive an answer that agrees pretty closely with Google.
y = example1850048.mph2kph(60)
Plus you could access the help text for either the method or the Constant property.
help example1850048.mph2kph
help example1850048.mph2kphFactor

Accedi per commentare.

Più risposte (2)

John D'Errico
John D'Errico il 14 Nov 2022
Modificato: John D'Errico il 14 Nov 2022
By far the simplest is to just stuff all of your variables into one struct. then pass it around to anything that will need it. This scheme is utterly simple, needing nothing more than one argument to be passed around.
You could do something more sophisticated of course. Create a function (I'll call it constantcache) that will store all of your constants inside, as persistent internally. Honestly, I'd use a struct inside there. Then if you need the value of myparam1, just call the function, telling it to return the desired parameter.
The usage mught look like this:
% first, initialize the cache of parameters. You might have a script
% somewhere that does this when you first start MATLAB.
datacache.E = 12; % Young's modulus
datacache.sigma = 5; % standard deviation
datacache.c = 300000; % speed of light in aluminum foil
% stuff the cache inside my function
constantcache(datacache)
% now we can use it anywhere, extracting ONLY the one parameter I might
% need at some point.
extractedparam = constantcache('sigma')
extractedparam = 5
% extract the entire cache as a struct
cache = constantcache('datacache')
cache = struct with fields:
E: 12 sigma: 5 c: 300000
An of course, if I failed to provide some parameter, it tells me.
myparam1 = constantcache('myparam1');
Error using solution>constantcache
The sky is falling! parameter myparam1 was not provided in the cache
The code might be as simple as this:
function returnedvalue = constantcache(arg)
persistent datacache
if isstruct(arg)
datacache = arg;
elseif isstr(arg)
if isequal(arg,'datacache')
% return the entire cache, to inspect what is stored.
returnedvalue = datacache;
elseif ~isfield(datacache,arg);
error("The sky is falling! parameter " + arg + " was not provided in the cache")
else
% return a specific parameter
returnedvalue = getfield(datacache,arg);
end
end
end
As you can see, there is no need for globals. They just drop you into a special level of programming hell, that not even Dante knew about. If you need to change some constant, then just call constantcache again, passing in the struct again.

Bruno Luong
Bruno Luong il 14 Nov 2022
Modificato: Bruno Luong il 14 Nov 2022
May be a little cumbersome but for serious application and when I'm not lazy, I define a subclass UniversalParameterClass of Singleton class.
classdef UniversalParameterClass < Singleton
properties % Public Access
PlankConstant;
...
end
end
Then the first time where I instantiate such class I populate the correct values in the properties (Note: you can set SetAccess of immutable or such).
Inside the function where I need a parameter I would do
UniversalParameter = UniversalParameterClass.instance();
PlankConstant = UniversalParameter.PlankConstant;
etc...
You can also pass UniversalParameter as input argument of the subfunction, if you don't want to call
UniversalParameter = UniversalParameterClass.instance();
again and again inside the subfunction.

Prodotti


Release

R2022a

Community Treasure Hunt

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

Start Hunting!

Translated by