Can I suppress command window output of a function in Matlab? - matlab

In Matlab I often use functions in my script that output something to the command window in one or several ways, for instance like this:
function output = mySubFunction(input)
disp(['the input is: ', num2str(input)])
output = input * 2;
disp(['the output is: ', num2str(output)])
end
If I now use this function in my script it would look something like this:
data = 5;
disp('applying transformation...')
transformation = mySubFunction(data);
disp(['the result of the transformation is: ', num2str(transformation)])
This is of course a very simple example, in fact the functions are often quite complicated and give a lot of output. In this case, to keep a better overview of what is going on in my main script using my command window, I would like to suppress the disp() functions in my mySubFunction. Is there any way that I can do this simply from my main script (that is, without diving into possibly very complicated functions and subfunctions to comment out all disp() functions or insert semicolons)?

I believe you want evalc.
From the MATLAB-documentation (the function is similar in Octave):
Use the "evalc" function to redirect all the output displayed on the
command window to a variable. This will not suppress figures, but it
does allow you to prevent print statements from being displayed in the
command window.
As an example:
[results, output] = evalc('mySubFunction(3)');
This displays nothing but stores the following variables in the workspace:
>> [results, output] = evalc('mySubFunction(3)');
>> ans
ans = 6
>> output
output = 6
>> results
results = the input is: 3
the output is: 6

The typical way to do this in any language as far as I'm concerned, not just matlab / octave, is to have a 'debug' or 'verbose' variable present, which is checked against an if statement and decides whether to print those logs. The if statement could be used in the relevant code directly, or wrapped inside a 'prettier' function responsible for doing the logging.
This variable may be passed explicitly to the function, or it could be a global variable. If you plan to run your script directly from the shell rather than directly from a matlab/octave environment, it could even be detected from from a shell-defined environmental variable. Alternatively, it could be read from a config file. It all depends on your use case.
For the sake of an example, here is your session, transformed as above, using the global variable approach.
%% in file mainScript.m
global VERBOSE
VERBOSE = true;
data = 5;
if VERBOSE, disp('applying transformation...'); end
transformation = mySubFunction(data);
if VERBOSE, disp(['the result of the transformation is: ', num2str(transformation)]); end
%% in file mySubFunction.m
function output = mySubFunction(input)
global VERBOSE
if VERBOSE, disp(['the input is: ', num2str(input)]); end
output = input * 2;
if VERBOSE, disp(['the output is: ', num2str(output)]); end
end

Related

Stop/pause MatLab execution for any underflow and overflow [duplicate]

Is it possible to add a customized dbstop condition to Matlab?
Recently I found myself with out of bounds values in multiple variables, one way to track down the first occurance of this would be to set a conditional breakpoint on each line where these values are updated. However, I hope there is an easier way to do this.
I have recently had to track down a NaN which was fairly trivial due to:
dbstop if naninf
Hence I hope that it is possible to get something like:
dbstop if anything outside myBound
or
dbstop if myVariable outside myBound
I would of course be willing to take the performance hit that one may expect.
If you use the editor, you can set a stop as normal, right-click on it, select "set/modify condition" and enter the condition (the stop will turn from red to yellow).
From command line, you can use
dbstop in file if expression
dbstop in file at location if expression
e.g.
dbstop in myFile at 200 if (~isempty(var) && var > 3)
as mentioned by #LuisMendo.
The second option may be more useful, since the first one seems to be only evaluated at the start of the file. In other words, it doesn't seem to be possible to have a similarly generic expression as dbstop if naninf that checks for certain values across an entire file.
The problem with using the form "DBSTOP in FILESPEC if EXPRESSION" of dbstop is that it sets a breakpoint only at the first line of the file. A solution is to use the form "DBSTOP in FILESPEC at LINENO if EXPRESSION" to set a breakpoint at each line.
Consider the following example script, saved on a file called testfile.m.
clear all
for m = 1:10;
k = 2*m
end
Say we want to stop if variable k exceeds the value 6. We first automatically set the breakpoints in all lines of this file:
file = 'testfile.m';
varname = 'k';
expression = 'k>6'; %// it should be 'exist(''k'')&&k>6', but that's added later
%// Determine number of lines of file:
fid = fopen('testfile.m');
cont = 1;
nlines = 0;
while cont
readline = fgetl(fid);
cont = ~isequal(readline,-1);
nlines = nlines + cont;
end
fclose(fid);
%// Set breakpoint at each line. We need eval for this
for n = 1:nlines
eval(['dbstop in ' file ' at ' num2str(n) ' if ( exist(''' varname...
''') && ( ' expression ' ) )'])
end
Now, after running the above (check that every line of testfile.m has a yellow breakpoint), run testfile and check values when it stops:
This is admittedly a little cumbersome if you have several variables or files. Also, I'm not sure how many simultaneous breakpoints Matlab supports (we are using one for each program line).
Thinking outside the box - you could write a class to hold your variable. There you could have a customized setter that will raise a warning if you violate boundaries. dbstop if warning should then be enough.

Getting variable out in workspace from function

When I am running a function, i do not get all variables out in
work-space. When i set breakpoints than only i am able to get
variables in workspace. Therefore, how to get variables out in workspace without setting breakpoint?
While the assignin trick is handy in certain situations, one should generally not make a habit of non-explicitly 'poofing' variables across namespaces. The more obvious first approach should be to specify outputs to your function, which is basic MATLAB syntax.
If you have a small number of outputs, you can specify them individually. For example:
function [c, d] = trialcode(a, b)
% Simple summation
c = a + b;
% Simple product
d = a * b;
end
If you have many outputs, once approach is to store the desired outputs into a structure or cell array so you are outputting a single variable rather than having to specify every output. Tweaking the above example:
function [output] = trialcode(a, b)
% Simple summation
output.c = a + b;
% Simple product
output.d = a * b;
end
You can use
assignin('base','variablename',value);
to write variables from your Function-Workspace to your base-workspace.
When you use breakpoints you get to see the Workspace of the function or script that the execution stopped at. You can also choose in your Editor which Workspace(stack) you want to see in the debug mode.
If you want to write your whole Function-Workspace into your base-workspace (which in the sense of encapsulation is not recommended) you can use
vars=whos;
for k=1:length(vars)
assignin('base', vars(k).name, eval(vars(k).name));
end

Prevent "input" function from calling functions or accessing variables

Considering the following code
x = input('Input an array: ');
If the user types [1 2 3], variable x will be assigned that numeric vector. Similarly, if they type {1, [2 3], 'abc'}, variable x will be a cell array containing those values. Fine.
Now, if the user types [sqrt(2) sin(pi/3)], variable x will be assigned the resulting values: [1.414213562373095 0.866025403784439]. That's because the provided data is evaluated by input:
input Prompt for user input.
result = input(prompt) displays the prompt string on the screen, waits
for input from the keyboard, evaluates any expressions in the input,
and returns the value in result. [...]
This can cause problems. For example, what happens if the user types addpath('c:\path\to\folder') as input? Since the input is evaluated, it's actually a
command which will be executed by Matlab. So the user can get to add a folder to the path. Worse yet, if they input path(''), the path will be effectively changed to nothing, and Matlab will stop working properly.
Another potential source of problems is that
[...] To evaluate expressions, input accesses variables in the current workspace.
For example, if the user inputs fprintf(1,'%f', varname) and varname is an existing numeric array, the user will know its current value.
This behaviour is probably by design. Users of a Matlab program are trusted when inputting data, much like they are trusted not to press Control-C to halt the program (and then issue all commands or inspect all variables they like!).
But in certain cases the programmer may want to have a more "secure" input function, by which I mean
prevent any function calls when evaluating user
input; and
prevent the input from accessing variables of the program.
So [1 2] would be valid input, but [sqrt(2) sin(pi/3)] or path('') would not because of item 1; and [1 2 3 varname(1)] would be invalid too because of item 2.
I have found a not very satisfactory solution (and I'd love to read about a better one). It uses a semi-documented function and implies saving the user input to a temporary file. The function, referred to in Yair Altman's blog, is getcallinfo. According to help getcallinfo:
getcallinfo
Returns called functions and their first and last lines
This function is unsupported and might change or be removed without
notice in a future version.
This function solves issue 1 (prevent function calls). As for issue 2 (prevent access to variables), it would suffice to evaluate the input within a function, so that it can't see other variables. Apparently (see example 2 below), getcallinfo detects not only called functions, but variables too. Anyway, it's probably a good idea to do the evaluation in the isolated scope of a function.
The procedure is then:
Use the string version of input to prevent evaluation:
x = input('Input an array: ', 's');
Save the string to a file:
filename = 'tmp.m';
fid = fopen(filename,'w');
fprintf(fid, '%s',x);
fclose(fid);
Check the input string with getcallinfo to detect possible function calls:
gci = getcallinfo(filename);
if ~isempty(gci.calls.fcnCalls.names)
%// Input includes function calls: ignore / ask again / ...
else
x = evalinput(x); %// evaluate input in a function
end
where evalinput is the following function
function x = evalinput(x)
x = eval(x);
Example 1
Consider
x = input('Input an array: ', 's');
with user input
[sqrt(2) sin(pi/3)]
Then
filename = 'tmp.m';
fid = fopen(filename,'w');
fprintf(fid, '%s',x);
fclose(fid);
gci = getcallinfo(filename);
produces a non-empty gci.calls.fcnCalls.names,
>> gci.calls.fcnCalls.names
ans =
'sqrt' 'sin' 'pi'
which tells us that the user input would call functions sqrt, sin and pi if evaluated. Note that operators such as / are not detected as functions.
Example 2
y = [10 20 30];
x = input('Input an array: ', 's');
User enters
[1 y y.^2]
Then
filename = 'tmp.m';
fid = fopen(filename,'w');
fprintf(fid, '%s',x);
fclose(fid);
gci = getcallinfo(filename);
produces
>> gci.calls.fcnCalls.names
ans =
'y' 'y'
So variables are detected by getcallinfo as if they were functions.
If I understand your question correctly, it is possible to use regular expressions to accomplish what you are trying to do.
No function or variable calls
At its simplest, this checks to making sure there are no alphabetical characters in the input string. The expression would then be, for x containing input:
expr = '[a-zA-Z]';
x = input('Input an array: ', 's');
valid = isempty(regexp(x,expr));
This alone works for the few examples you give above.
Allowing some functions or variables
Suppose you want to allow the user to access some variables or functions, maybe simple trig functions, or pi or what have you, then it's no longer so simple. I've been playing around with an expression like below:
expr = '(?!cos\(|pi|sin\()[a-zA-Z]+
But it doesn't quite do what is expected. It will match in( in sin. If you know regex better than I, you can massage that to work properly.
Otherwise, an alternative would be to do this:
isempty(regexp(regexprep(x,'(sin\(|cos\(|pi|x)',''),expr))
so that you remove the strings you are interested in.
Hope this helps.
Update: In order to allow imaginary/exp values, and paths
The new expression to match becomes
expr = '[iIeE][a-zA-Z]+';
This ignores i/I and e/E (you can augment this as you see fit). You can also do a two character limit by switching to \{2,}, though I people can still have one character anonymous functions..
The other part, to check for input becomes:
isempty(regexp(regexprep(x,'(sin\(|cos\(|pi|x|''(.*?)'')',''),expr))
now you can exclude custom functions (you can always have this as an array and join them together by a |) and paths.
Here are a few examples tested along with the usual:
Passes
'[1+2i, 34e12]'
'''this is a path'''
'[cos(5), sin(3+2i)]'
Fails
'[1+2ii, 34e12]'
'this is not a path'
'''this is a path'' this is not'

Customize dbstop in MATLAB

Is it possible to add a customized dbstop condition to Matlab?
Recently I found myself with out of bounds values in multiple variables, one way to track down the first occurance of this would be to set a conditional breakpoint on each line where these values are updated. However, I hope there is an easier way to do this.
I have recently had to track down a NaN which was fairly trivial due to:
dbstop if naninf
Hence I hope that it is possible to get something like:
dbstop if anything outside myBound
or
dbstop if myVariable outside myBound
I would of course be willing to take the performance hit that one may expect.
If you use the editor, you can set a stop as normal, right-click on it, select "set/modify condition" and enter the condition (the stop will turn from red to yellow).
From command line, you can use
dbstop in file if expression
dbstop in file at location if expression
e.g.
dbstop in myFile at 200 if (~isempty(var) && var > 3)
as mentioned by #LuisMendo.
The second option may be more useful, since the first one seems to be only evaluated at the start of the file. In other words, it doesn't seem to be possible to have a similarly generic expression as dbstop if naninf that checks for certain values across an entire file.
The problem with using the form "DBSTOP in FILESPEC if EXPRESSION" of dbstop is that it sets a breakpoint only at the first line of the file. A solution is to use the form "DBSTOP in FILESPEC at LINENO if EXPRESSION" to set a breakpoint at each line.
Consider the following example script, saved on a file called testfile.m.
clear all
for m = 1:10;
k = 2*m
end
Say we want to stop if variable k exceeds the value 6. We first automatically set the breakpoints in all lines of this file:
file = 'testfile.m';
varname = 'k';
expression = 'k>6'; %// it should be 'exist(''k'')&&k>6', but that's added later
%// Determine number of lines of file:
fid = fopen('testfile.m');
cont = 1;
nlines = 0;
while cont
readline = fgetl(fid);
cont = ~isequal(readline,-1);
nlines = nlines + cont;
end
fclose(fid);
%// Set breakpoint at each line. We need eval for this
for n = 1:nlines
eval(['dbstop in ' file ' at ' num2str(n) ' if ( exist(''' varname...
''') && ( ' expression ' ) )'])
end
Now, after running the above (check that every line of testfile.m has a yellow breakpoint), run testfile and check values when it stops:
This is admittedly a little cumbersome if you have several variables or files. Also, I'm not sure how many simultaneous breakpoints Matlab supports (we are using one for each program line).
Thinking outside the box - you could write a class to hold your variable. There you could have a customized setter that will raise a warning if you violate boundaries. dbstop if warning should then be enough.

How to wrap an already existing function with a new function of the same name

Is it possible to create a wrapper around a function that has the exact same name as the original function?
This would be very useful in circumstances where the user wants to do some additional checks on input variables before they are passed on to the built in function How to interrupt MATLAB IDE when it hangs on displaying very large array?
Actually alternatively to slayton's answer you don't need to use openvar. If you define a function with the same name as a matlab function, it will shadow that function (i.e. be called instead).
To then avoid recursively calling your own function, you can call the original function from within the wrapper by using builtin.
e.g.
outputs = builtin(funcname, inputs..);
Simple example, named rand.m and in the matlab path:
function out = main(varargin)
disp('Test wrapping rand... calling rand now...');
out = builtin('rand', varargin{:});
Note that this only works for functions that are found by builtin. For those that are not, slayton's approach is likely necessary.
Yes this is possible but it requires a bit of hacking. It requires that you copy around some function handles.
Using the example provided in the question I will show how to wrap the function openvar in a user defined function that checks the size of the input variable and then allows the user to cancel any open operation for variables that are too large.
Additionally, this should work when the user double clicks a variable in the Workspace pane of the Matlab IDE.
We need to do three things.
Get a handle to the original openvar function
Define the wrapper function that calls openvar
Redirect the original openvar name to our new function.
Example Function
function openVarWrapper(x, vector)
maxVarSize = 10000;
%declare the global variable
persistent openVarHandle;
%if the variable is empty then make the link to the original openvar
if isempty(openVarHandle)
openVarHandle = #openvar;
end
%no variable name passed, call was to setup connection
if narargin==0
return;
end
%get a copy of the original variable to check its size
tmpVar = evalin('base', x);
%if the variable is big and the user doesn't click yes then return
if prod( size( tmpVar)) > maxVarSize
resp = questdlg(sprintf('Variable %s is very large, open anyway?', x));
if ~strcmp(resp, 'Yes')
return;
end
end
if ischar(x) && ~isempty(openVarHandle);
openVarHandle(x);
end
end
Once this function is defined then you simply need to execute a script that
Clears any variables named openvar
run the openVarWrapper script to setup the connection
point the original openVar to openVarWrapper
Example Script:
clear openvar;
openVarWrapper;
openvar = #openVarWrapper;
Finally when you want to clean everything up you can simply call:
clear openvar;
I prefer jmetz's approach using builtin() when it can be applied, because it is clean and to the point. Unfortunately, many many functions are not found by builtin().
I found that I was able to wrap a function using a combination of the which -all and cd commands. I suspect that this approach can be adapted to a wide variety of applications.
In my example case, I wanted to (temporarily) wrap the interp1 function so that I could check for NaN output values. (The interp1 function will, by default, return a NaN under some conditions, such as if a query point is larger than the largest sample point.) Here's what I came up with:
function Vq = interp1(varargin)
persistent interp1_builtin;
if (isempty(interp1_builtin)) % first call: handle not set
toolbox = 'polyfun';
% get a list of all known instances of the function, and then
% select the first such instance that contains the toolbox name
% in its path
which_list = which('interp1','-all');
for ii = 1:length(which_list)
if (strfind(which_list{ii}, [filesep, toolbox, filesep]))
base_path = fileparts(which_list{ii}); % path to the original function
current_path = pwd;
cd(base_path); % go to the original function's directory
interp1_builtin = #interp1; % create a function handle to the original function
cd(current_path); % go back to the working directory
break
end
end
end
Vq = interp1_builtin(varargin{:}); % call the original function
% test if the output was NaN, and print a message
if (any(isnan(Vq)))
dbstack;
disp('ERROR: interp1 returned a NaN');
keyboard
end
end
See also: How to use MATLAB toolbox function which has the same name of a user defined function