Declare global variables in a loop in MATLAB - matlab

Is it possible to declare global variables in MATLAB inside a loop:
cellvar = { 'ni' ; 'equity' ; 'assets' } ;
for i = 1:size(cellvar,1)
global cellvar{1} % --> THIS GIVES AN ERROR
end
% Desired result:
global ni
global equity
global assets
Matlab documentation says: "There is no function form of the global command (i.e., you cannot use parentheses and quote the variable names)." Any suggested work-around? Thanks!

You can use the EVAL function to do this:
for var = 1:numel(cellvar)
eval(['global ' cellvar{var}]);
end
Also, since GLOBAL accepts a command-line list of variable names, you could avoid the for loop by using SPRINTF to concatenate your variable names into one string to be evaluated:
eval(['global' sprintf(' %s',cellvar{:})]);

Related

Is there a way to declare variable local in nested functions?

Is there a way to declare a variable local in a nested function?
By declare I mean name a variable as usual but enforce that its scope starts in-place. Imagine creating a new nested function in the middle of a large program. There are natural variable names you wish to use and you would not want to worry whether you have to check existing variable names every single time you create a new variable.
To describe the desired effect, I'll use two examples. One minimal. One shows the problem a little better visually.
Short example
function fn1
var = 1
function fn2
local var = 'a';
function fn3
end
end
end
Within fn2 and fn3, var refers to the new variable with starting value 'a' while outside fn2, var with starting value 1 is still available as usual.
Long example
function fn1
var = 1;
var2 = 2;
function fn2
var2 = 'I can access var2 from fn1. Happy.'
local var = 'a'; % remove local to run this snippet
fn3;
function fn3
var2 = 'I can access var2 from fn1. Happy.'
var = 'fn2 cannot safely use variable name var because it may have been used in fn1. But var is the natural name to use in fn2. Sad.';
var = 1;
var2 = 2;
end
end
function fn4
var2 = 'I can also access var2 from fn1. Also happy.'
var = 'If only local scoping works, I would still be able to access var. Would be happy.';
end
fn4;
fn2;
var,
var2,
end
%% desired output, but not real
>> fn1;
var =
1
var2 =
2
Is there a way to accomplish the above?
Currently, I do my best ensure that name variables that are not local in nature with special non-generic names and name variables that are obviously local in nature temp# where # is an integer. (I suppose clearing variables after known last use can help sometimes. But I'd rather not have to do that. Local variables are too numerous.) That works for small programs. But with larger programs, I find it hard to avoid inadvertently re-writing a variable that has already been named at a higher scoping level. It also adds a level of complexity in the thought process, which is not exactly good for efficiency, because when creating a program, not all variables are either obviously local or obviously not local. A flexible scoping mechanism would be very helpful.
I write my programs in Sublime Text so I am not familiar with the Matlab editor. Does the editor have visual guards/warning prompts against errors arising from inflexible scoping? Warning that requires visually scanning through the whole program is barely useful but at least it would be something.
No, there is no way in MATLAB to declare a nested function variable to be local to that function if the variable also exists in the external scope (i.e., the function containing the nested function).
The behavior of Nested Functions is fully described in MATLAB documentation and what you are asking is not possible or at least not documented.
It is specifically stated that the supported behavior is
This means that both a nested function and a function that contains it can modify the same variable without passing that variable as an argument.
and no remedy to prevent this behavior is mentioned in the documentation.
Variables that are defined as input arguments are local to the function. So you can define var as an input argument of fn2*:
function fn2 (var)
...
end
However if you like to define fn2 without changing its signature you need to define an extra level of nesting:
function fn1
var = 1;
var2 = 2;
function fn2
fn2_impl([]);
function fn2_impl (var)
var2 = 'I can access var2 from fn1. Happy.'
var = 'a'; % remove local to run this snippet
fn3;
function fn3
var2 = 'I can access var2 from fn1. Happy.'
var = 'fn2 cannot safely use variable name var because it may have been used in fn1. But var is the natural name to use in fn2. Sad.';
var = 1;
var2 = 2;
end
end
end
function fn4
var2 = 'I can also access var2 from fn1. Also happy.'
var = 'If only local scoping works, I would still be able to access var. Would be happy.';
end
fn4;
fn2;
var,
var2,
end
Here fn2_impl is the actual implementation of fn2 and it inherits all variables that inherited by fn2. var is local to fn2_impl because it is an input argument.
However, I recommend local functions as suggested by #CrisLuengo. If variables need to be shared, using OO style of programming is more readable and maintainable than implicitly sharing by nested functions.
Thanks to #CrisLuengo that noted me that it is possible to skip inputs arguments when calling MATLAB functions.
Nested functions have a very specific use case. They are not intended to avoid having to pass data into a function as input and output arguments, which is to me what you are attempting. Your example can be written using local functions:
function fn1
var = 1;
var2 = 2;
[var,var2] = fn4(var,var2);
var2 = fn2(var2);
var,
var2,
end
function var2 = fn2(var2)
var2 = 'I can access var2 from fn1. Happy.'
var = 'a'; % remove local to run this snippet
[var,var3] = fn3(var,var2);
end
function [var,var2] = fn3(var,var2)
var2 = 'I can access var2 from fn1. Happy.'
var = 'fn2 cannot safely use variable name var because it may have been used in fn1. But var is the natural name to use in fn2. Sad.';
var = 1;
var2 = 2;
end
function [var,var2] = fn4(var,var2)
var2 = 'I can also access var2 from fn1. Also happy.'
var = 'If only local scoping works, I would still be able to access var. Would be happy.';
end
The advantage is that the size of fn1 is much reduced, it fits within one screen and can much more easily be read and debugged. It is obvious which functions modify which variables. And you can name variables whatever you want because no variable scope extends outside any function.
As far as I know, nested functions can only be gainfully used to capture scope in a lambda (function handle in MATLAB speak), you can write a function that creates a lambda (a handle to a nested function) that captures a local variable, and then return that lambda to your caller for use. That is a powerful feature, though useful only situationally. Outside that, I have not found a good use of nested functions. It’s just something you should try to avoid IMO.
Here's an example of the lambda with captured data (not actually tested, it's just to give an idea; also it's a rather silly application, since MATLAB has better ways of interpolating, just bear with me). Create2DInterpolator takes scattered x, y and z sample values. It uses meshgrid and griddata to generate a regular 2D grid representing those samples. It then returns a handle to a function that interpolates in that 2D grid to find the z value for a given x and y. This handle can be used outside the Create2DInterpolator function, and contains the 2D grid representation that we created. Basically, interpolator is an instance of a functor class that contains data. You could implement the same thing by writing a custom class, but that would require a lot more code, a lot more effort, and an additional M-file. More information can be had in the documentation.
interpolator = Create2DInterpolator(x,y,z);
newZ = interpolator(newX,newY);
function interpolator = Create2DInterpolator(x,y,z)
[xData,yData] = meshgrid(min(x):max(x),min(y):max(y));
zData = griddata(x,y,z,xData,yData);
interpolator = #InterolatorFunc;
function z = InterolatorFunc(x,y)
z = interp2(xData,yData,zData,x,y);
end
end

Call a script with definitions in a function

We have a script that defines values to names similar to #define in c. For example:
script.m:
ERR_NOERROR = 0;
ERR_FATAL = 1;
This script already exists and is used for value replacement when reading data from files.
Now we have a function (or more) that does some analysis and we would like to use the same definition in this function to avoid magic numbers. But when the script is called from the function we get an error.
Attempt to add "ERR_NOERROR" to a static workspace.
See MATLAB Programming, Restrictions on Assigning to Variables for details.
And this does not help much in the understanding of the problem.
The question is how can we make these definitions visible/usable in the functions with having to copying it every time.
Example:
function foo = bar(a)
run(script.m) %also tried running it without the run command
if a == ERR_NOERROR
foo = 5;
else
foo = 6;
end
end
edit:
There was a nested function,below in the function which I was not aware of. This explains the problem.
This kind of scoping error happens when you use nested or anonymous function within a function. The solution is well documented.
To your case, you can avoid nested function, or "Convert the script to a function and pass the variable using arguments", as the documentation suggests.
EDIT: I should have made it clear that the error occurs even if the script is not called within the nested function. Similar scenario is that, in debug mode (by setting up a break point), it will be an error if one tries to create a temporal variable to test something.
This is not a direct answer, rather a recommendation to switch to another method, which will not be mixing scope and workspace.
Instead of defining your constant in a script, you could make a class containing only constant properties. ex: code for error_codes.m:
classdef error_codes
% ---------------------------------------------------------------------
% Constant error code definition
% ---------------------------------------------------------------------
properties (Constant = true)
noerror = 0 ;
fatal = 1 ;
errorlvl2 = 2 ;
errorlvl3 = 3 ;
warning = -1 ;
% etc ...
end
end
I use this style for many different type of constants. For tidiness, I groups them all in a Matlab package directory (The directories which starts with a + character.
The added benefit of using constant class properties is the safety that the values cannot be changed in the middle of the code (your variables defined in a script could easily be overwritten by a careless user).
So assuming my file error_codes.m is placed in a folder:
\...somepath...\+Constants\error_codes.m
and of course the folder +Constants is on the MATLAB path, then to use it as in your example, instead of calling the script, just initialise an instance of the class, then use the constant values when you need them:
function foo = bar(a)
ERR = Constants.error_codes ;
if a == ERR.noerror
foo = 5;
else
foo = 6;
end
or it can works in switch statement too:
switch a
case ERR.noerror
foo = 5 ;
case ERR.warning
foo = 42 ;
case ERR.fatal
foo = [] ;
end

Is it possible to write a MATLAB script that can give command line input to a function?

I am writing MATLAB code that will fit together with other MATLAB functions that I cannot modify. Some of these existing functions take input from the command line. Is there a way I can write a test script in MATLAB that can call these functions, and then provide the input as the user would?
ie. if I have a function:
function y = f(x)
z = input('Enter number: ');
y = x + z;
end
Is there a way to have a script call f and provide z?
If you are looking for a non elegant solution.
If you are looking for a potentially dangerous solution.
Then you might try this: write a function named "input" as follows:
function a=input(str)
% THIS IS THE DUMMY VERSION OF THE
% MATLAB BUILT-IN FUNCTION "input"
global dummy_input
disp('WARNING!!!')
disp('MATLAB "input" built-in function overridded')
disp(['Setting dummy_inpt'])
a=dummy_input;
end
Declare a global variable either in the script you use to test the function and in your "dummy" input function.
Assign the desired value to the global variable as follows:
global dummy_input
x=3;
dummy_input=123;
y=my_func(x)
dummy_input=42.13;
y=my_func(x)
If my_func is the function you post in the question, you will obtain:
WARNING!!!
MATLAB "input" built-in function overridded
Setting dummy_inpt
y =
126
WARNING!!!
MATLAB "input" built-in function overridded
Setting dummy_inpt
y =
45.1300
I've added the printing of the warnings in the "dummy" input function yust as a remainder ...
You do not need to modify the function you want to test, when it will call input to get a number from the user, it will call your "dummy" input.
Version 2 of the "dummy" input function
This version of the "dummy" input function allows autonatically handling multiple request of input values.
It requires the user knows in advance how many times the "original" input function is called.
No additional global counter is required.
It is sufficient the change the definition of the global parameter in the script, declaring it as an array containing the set of input the user want to assign:
global input_list
input_list=[27 30 5 31 21]
In the "dummy" input function, the first element of the array is assigned to the output variable, then the it is deleted:
a=input_list(1);
input_list(1)=[];
the code of the updated version of the function is the following:
function a=input(str)
% THIS IS THE DUMMY VERSION OF THE
% MATLAB BUILT-IN FUNCTION "input"
global input_list
disp('WARNING!!!')
disp('MATLAB "input" built-in function overridded')
disp(' ')
disp(' ')
disp(' ')
if(isempty(input_list))
error('Error in DUMMY input: no more input data')
else
disp(['Setting dummy_input ' num2str(input_list(1))])
a=input_list(1);
disp(' ')
disp(' ')
disp(' ')
input_list(1)=[];
end
end
An error is generated in case the input array becomes empty (by deleting its element at each call) before the end of the script.
I've also added some calls to disp to make more "clear" the output on the Command Window.
Also the "dummy" input function print a message on the Command Window telling which input values has been assigned.
Make sure to remove your dummy "input" function at the end
Hope this helps.

How can I make global a variable in a function m.file?

I made global a variable in main m.file and used that variable in a function m.file and I had no problem. But, I wanna make global a variable in function m.file and use that variable in main m.file.
For this aim, I wrote:
function cost=MY_Fun(X)
global m
.
.
end
in function m.file, and wrote "global m" in main m.file. But, I get m=[]! How can I do that, so that the main m.file could correctly find the "m" value?
In the main script, if you are using m before the function call to MY_Fun, it would be empty. But after it, would have the value assigned inside MY_Fun. m would get the value from MY_Fun, only after the function gets called from the main script. The following codes might help you understand.
Main Script
global m
m_before_fun = m
cost1=MY_Fun(1);
m_after_fun = m
Function
function cost=MY_Fun(X)
%%// Declare m as a global variable
global m
%%// Assign some value to m
m = 10;
cost = 1;
return; %%// I prefer RETURN to END
Output
m_before_fun =
[]
m_after_fun =
10

Global variable does not have global scope

supposedlyGlobalVariable := "blah"
ARoutine()
{
localVariable := "asdf"
MsgBox, The global variable value is %supposedlyGlobalVariable%. The local variable value is %localVariable%.
}
^!X:: ;This assigns the hotkey CTRL + ALT + X to run the routine
ARoutine()
return
Run the code and the result is:
"The global variable value is . The local variable value is asdf."
The documentation states:
Variable scope and declarations: With the exception of local variables
in functions, all variables are global; that is, their contents may be
read or altered by any part of the script.
Why does my global variable not have scope within the function?
The documentation for global variables can be found here:
https://autohotkey.com/docs/Functions.htm#Global
Global variables
To refer to an existing global variable inside a function (or create a
new one), declare the variable as global prior to using it. For
example:
LogToFile(TextToLog)
{
global LogFileName
FileAppend, %TextToLog%`n, %LogFileName%
}
I believe the concept of global, with AHK, is a bit different than in other languages. With AHK you can create a variable and use it within multiple hotkeys, and subroutines, without declaring it as global.
Gv := 0
f1::SetTimer, Action, % (on:=!on) ? (1000) : ("Off")
Action:
Gv++
trayTip,, % Gv
Return
f2::Msgbox, % Gv
Explaination of code:
The F1 key toggles a timer to run the subroutine: Action every 1000ms.
% starts an expression.
on:=!on reverses the binary value of variable on every time F1 is pressed.
?: together is called the ternary operator.
When on=1 delay is set to 1000ms; when on=0 the timer is turned Off.
The ++ operator adds 1 to variable Gv.
This makes things easier:
https://www.autohotkey.com/docs/Functions.htm#SuperGlobal
Super-global variables [v1.1.05+]: If a global declaration appears
outside of any function, it takes effect for all functions by default
(excluding force-local functions). This avoids the need to redeclare
the variable in each function. However, if a function parameter or
local variable with the same name is declared, it takes precedence
over the global variable. Variables created by the class keyword are
also super-global.
Just declare your variable as global in the main script:
global supposedlyGlobalVariable := "blah"
P.Brian, It works when you do this.. I know it doesn't explain why, but this might be your workaround.
#Persistent
GlobalVariable = "blah"
RETURN
ARoutine:
{
localVariable := "asdf"
MsgBox, The global variable value is %GlobalVariable%. The local variable value is %localVariable%.
}
Return
^!X:: ;This assigns the hotkey CTRL + ALT + X to run the routine
gosub, ARoutine
return
You just need to declare the variable as global inside your function
supposedlyGlobalVariable := "blah"
ARoutine()
{
global supposedlyGlobalVariable
localVariable := "asdf"
MsgBox, The global variable value is %supposedlyGlobalVariable%. The local variable
value is %localVariable%.
}
^!X:: ;This assigns the hotkey CTRL + ALT + X to run the routine
ARoutine()
return