Matlab - Do action after every line - matlab

Is there an elegant way to tell matlab to perform a predefined action after the execution of every line in a certain script? By elegant I mean no calling of the action after every line, but rather something like a simple command given at the start of the script.
Example:
Action --> disp('Performing Action');
script:
a = 1;
b = 2;
c = 3;
So the desirable outcome is that after each assignment (of a, b and c), the disp() command would be performed.

You can automatically create a modifed file that has the desired action included at the end of each line:
action = 'disp(''Performing Action'');'; %// action to be added at the end of each line
file = 'script.m'; %// original script
file_out = 'script_out.m'; %// modified script with action added
x = importdata(file); %// cell array of strings. Each line of the
%// original file is a string
x = strcat(x, {' ; '}, action); %// add action at the end of each string,
%// preceded with `;` in case the original line
%// didn't include that
fid = fopen(file_out, 'w');
fprintf(fid, '%s\n', x{:}); %// write output file
fclose(fid);

a = 1;
disp('Performing Action');
b = 2;
disp('Performing Action');
c = 3;
disp('Performing Action');
Or, if you can do this in a loop
for ii = 1:3
a(ii) = ii;
disp('Performing Action');
end
Actually making it output something after every line is not very matlab, but you could of course just loose all the semicolons and thus make it display all variables if you want to track where in the script you are.
I'd suggest a verbose switch in your code. Set it to 0 for no output and 1 for output (or use multiple levels if so desired)
if verbose > 0
disp('Performing Action');
end
This way you can easily switch the output on or off, depending on need.
For the code-reading and appending piece, see Louis Mendo's answer at https://stackoverflow.com/a/32137053/5211833

Here is my attempt. You can:
read the function line by line
execute each line, followed by your custom function.
NOTE
This won't work with functions containing for, if, etc..
You can eventually improve the code by passing a function handler with your custom action to runEachLine (and feval with that).
Here a code sample (partly based on this):
foo.m
function foo(args)
a = args{1};
b = 2;
c = a + b;
end
runEachLine.m
function runEachLine( mfile, args )
if nargin < 1
error('No script m-file specified.');
end
if ~strcmp(mfile(end-1:end),'.m')
mfile = [mfile '.m'];
end
if ~exist(mfile,'file')
error(['Cannot access ' mfile])
end
% Read function file
M = textread(mfile,'%s','delimiter','\n');
% Remove empty lines
M = M(~cellfun('isempty',M));
% Input arguments
assignin('base', 'args', args);
% Skipping first line: function [...] = func_name(...)
% Skipping last line : end
for k=2:length(M)-1
try
% Execute each line
evalin('base',M{k})
% Execute your custom function
disp(['Performing Action: ' M{k}]);
catch ME
error('RunFromTo:ScriptError',...
[ME.message '\n\nError in ==> ' mfile ' at ' num2str(k) '\n\t' M{k}]);
end
end
end
Usage:
>>runEachLine('foo.m', {4});
Result:
>> runEachLine('foo.m', {4})
Performing Action: a = args{1};
Performing Action: b = 2;
Performing Action: c = a + b;
>>

No, there is not.
What you present in your original question is just a simple usage of the = operator.
If you want to overload the default behaviour of MATLAB then you should consider creating a class and define the desired behaviour for each operator or function needed.

Related

Get name of code section in Matlab script

I have a script (actually a test script used for script based unit tests) which is divided into sections of the type
%% test foo
code
%% test bar
more code
How do I retrieve the name of a section (in any form, e.g. foo, test foo, TestFoo or whatever Matlab makes out of a section name) from inside my script (i.e. the place where I have written code).
Sorry, I have no Matlab on this computer. So the code is untested. But I guess with the code snippet below you should be able to do what you are trying.
It's a little bit hacky, but it's Matlab.
function section_name = get_current_section_name()
ds = dbstack();
execution_file = ds(2).file;
execution_line = ds(2).line;
fid = fopen(execution_file);
section_name = "";
current_line_number = 1;
tline = fgetl(fid);
while ischar(tline)
if startsWith(tline, "%%")
section_name = tline;
end
if execution_line == current_line_number
return
end
tline = fgetl(fid);
current_line = current_line + 1;
end
fclose(fid)
% case: not found
section_name = "";
end
And on your desired position you should be fine if you just call
section_name = get_current_section_name()
Here's a possible solution.
Given the following code:
%% test foo
code
%% test bar
more code
%% test baz
other code
You can obtain the comment of the active live script as follows:
>> X = matlab.desktop.editor.getActive; % Get MATLAB editor
>> editorText = X.Text; % Get editor text
>> n = X.Selection(1); % Get line number on which cursor is
>> all_lines = regexp(editorText,'\n','split'); % Get all lines of editor
>> for line = all_lines(n:-1:1) % Iterate backwards from starting lne
>> if contains(line,'%%') % If the line contains a comment, extract string and exit loop
>> match = regexp(line,'%% (.+)$','tokens','once')
>> break
>> end
>> end
match is a cell with the desired content. If your cursor is in the first code section, match{1} contains test foo. If the cursor is in the second block, test bar and finally test baz for the last block.
How it works:
First we get the active MATLAB editor and the position of the cursor. Then, we split on newline \n to obtain all of the lines in the editor. From the current line, we iterate backwards to identify the first line which has the comment, which contains the desired string.

Printing current code line and specific variables' names, types and dimensions

To debug my Octave/MATLAB code, I want to be able to do something like:
A = magic(3);
b = 42;
describe(#A, #b);
and get output like:
filename.m line 3: "A" is a 3x3 matrix
filename.m line 3: "b" is a scalar of value: 42
For multiple variables, how can I print the:
Calling function's file and line
Name
Type
Dimensions
Overview
In this answer I list 3 subtly different versions of the function describe.
Takes any number of variables and creates an output string and displays it using fpritnf
Takes any number of variables and creates an output cell array and displays it using disp
Takes any number of variable names and creates an output string as in 1. This has the advantage of being able to deal with indexing like describe('myvar{1}').
Main functionality description and version 1.
You can use various standard functions to get the info you want:
varargin to accept variable number of input variables
dbstack to get the file name / current line
inputname to get the names of inputs as they are passed into describe
fprintf to display with new line characters
varargout to optionally return or display the result
So create your describe function like so:
function varargout = describe(varargin)
% varargin used to accomodate variable number of inputs
% By default, at least get functions stack (even if no variables are passed in)
st = dbstack;
% Convert cell output to string (excluding describe.m itself)
outstring = '';
% Loop backwards for more logical order (most recent last)
for ii = size(st, 1):-1:2
% new line character at end only works with fprintf, not disp
outstring = [outstring, st(ii).file, ' > ', st(ii).name, ...
', line ', num2str(st(ii).line), '\n'];
end
% Loop over variables and get info
for n = 1:nargin
% Use inputname to get name of input variables
outstring = [outstring, '"', inputname(n), '" is a ', ...
class(varargin{n}), ' of size ', mat2str(size(varargin{n})), '\n'];
end
% If output variable is requested then assign to output variable
% If not, just display output string to command window
if nargout
varargout{1} = outstring;
else
fprintf(outstring)
end
end
The only tweaking required here really is formatting, all of your requested functionality is here and hopefully enough flexibility is built in to handle your needs.
Example output:
% In myMainFunction.m, in the subfunction mySubFunction
% Could store output using 'd = describe(A,B);' and use 'fprintf' later
describe(A, B);
% In the command window
myMainFunction.m > myMainFunction, line 3
myMainFunction.m > mySubFunction, line 39
"A" is a double of size [1 3]
"B" is a double of size [1 5 9 7]
Tested in Matlab R2015b, all functions listed above existed since before R2006a according to documentation so I assume it's likely that they have Octave equivalents.
Version 2.
Cell instead of string with line separators.
This has a less pretty output but is also perhaps a less clunky method, assigning the strings to a cell array rather than having to rely on fprintf for new lines. It can be done easily using the following (uncommented for brevity) version.
function varargout = describe(varargin)
st = dbstack; outcell = {};
for ii = size(st, 1):-1:2
outcell{end+1} = [st(ii).file, ' > ', st(ii).name, ', line ', num2str(st(ii).line)];
end
for n = 1:nargin
outcell{end+1} = ['"', inputname(n), '" is a ', class(varargin{n}), ' of size [', size(varargin{n}), ']'];
end
outcell = outcell.'; % Transpose to make it a column cell array
disp(outcell)
end
Version 3.
Passing variable names as strings, so things like 'myvar(1)' are displayed.
This uses evalin to evaluate the variables in the caller workspace (where describe was called from). NOTE: This could be more memory intensive as you are recreating the variables in this describe function to get their attributes.
function varargout = describe(varargin)
% varargin used to accomodate variable number of input names
st = dbstack;
outstring = '';
for ii = size(st, 1):-1:2
outstring = [outstring, st(ii).file, ' > ', st(ii).name, ', line ', num2str(st(ii).line), '\n'];
end
% Loop over variables and get info
for n = 1:nargin
% Variables are passed by name, so check if they exist
try v = evalin('caller', varargin{n});
outstring = [outstring, '"', varargin{n}, '" is a ', class(v), ' of size ', mat2str(size(v)), '\n'];
catch
outstring = [outstring, 'Variable "', varargin{n}, '" not found!\n'];
end
end
fprintf(outstring)
end
Example use:
% This can be used with indexed variables too. MUST PASS STRINGS!
describe('C{1}', 'B(1:2, :)')
% In the command window
myMainFunction.m > myMainFunction, line 3
myMainFunction.m > mySubFunction, line 39
"C{1}" is a double of size [1 3]
"B(1:2, :)" is a double of size [2 5]
% Because you're passing strings, you can use command syntax if you want
% Gives same result but don't have to pass strings
% as that's how inputs are interpreted anyway for command syntax.
describe C{1} B(1:2, :)
I use something similar myself. Here's mine:
function describe(A)
fprintf(' Class : %s\n',class(A));
fprintf(' Num. Elems : %s\n',num2str(numel(A)));
fprintf(' Size : %s\n',num2str(size(A)));
fprintf(' Total Min : %s\n',num2str(min (A(:))));
fprintf(' Total Max : %s\n',num2str(max (A(:))));
fprintf(' Total Sum : %s\n',num2str(sum (A(:))));
fprintf(' Total Mean : %s\n',num2str(mean(A(:))));
fprintf('Total St.Dev : %s\n',num2str(std (A(:), 1)));
fprintf(' Unique vals : %s\n',num2str(length(unique(A))));
end
Edit: I'm aware this isn't a literal answer to what you ask, but I use this all the time and thought it might be useful to share. :)
PS. Having said that, it has never occurred to me that I might ever want to use such a function in a non-interactive way: if I need to inspect variables in this manner, I just put a breakpoint (or keyboard instruction) in the code and then inspect stuff in the terminal at the point where it's most relevant, so reporting a filename and a linenumber manually has never occurred to me! What is your use-case that you need to perform non-interactive debugging like this? If it's for post-mortem "testing" purposes, you really should be writing proper tests and sanity checks instead anyway!
Also, this is only for single variables, but I find that preferable; it's an extremely simple one-liner loop if you wanted more anyway.
You can use size to have the size of your variable, for the type use class and for the name use inputname.
For example:
function describe(a)
s = size(a); % Return the size of a
t = class(a); % Return the type of a
name = inputname(1); % Return the name of a
printf(['filename.m line 3: ''''' name ''''' size:' s(1) 'x' s(2) ' and type: ' t]);
end
I don't know how to use the name of file and the line, and if you want another way to display this you can can use if condition to seperate scalar from vector from matrix.

Distinguish between scripts and functions programmatically

Given a file name, how can I programmatically distinguish between scripts and functions in MATLAB?
If I attempt to pass an argument to a script, I get Attempt to execute SCRIPT somescript as a function:. Is there a way to detect this without attempting to execute it?
Update: As #craq pointed out, shortly after this question was posted, there was an article about this on MATLAB Central: http://blogs.mathworks.com/loren/2013/08/26/what-kind-of-matlab-file-is-this/
Didn't find a clean solution, but you can probably use try-catch (as #Ilya suggested) and nargin
EDIT - Use function to avoid some naming conflict; use exist to further classify the input (e.g. MEX-file)
function is_script = is_a_script( varargin )
% is_a_script( varargin ) returns one of the following:
% 1: if the input is a script
% 0: if the input is a function
% -1: if the input is neither a function nor a script.
is_script = 0;
switch( exist(varargin{1}) )
case 2
% If the input is not a MEX or DLL or MDL or build-in or P-file or variable or class or folder,
% then exist() returns 2
try
nargin(varargin{1});
catch err
% If nargin throws an error and the error message does not match the specific one for script, then the input is neither script nor function.
if( strcmp( err.message, sprintf('%s is a script.',varargin{1}) ) )
is_script = 1;
else
is_script = -1;
end
end
case {3, 4, 5, 6} % MEX or DLL-file, MDL-file, Built-in, P-file
% I am not familiar with DLL-file/MDL-file/P-file. I assume they are all considered as functions.
is_script = 0;
otherwise % Variable, Folder, Class, or other cases
is_script = -1;
end
If you are willing to use semi-documented features, here is something to try:
function tf = isfunction(fName)
t = mtree(fName, '-file');
tf = strcmp(t.root.kind, 'FUNCTION');
end
This is the same function used in MATLAB Cody and Contests to measure code length.
This is a bit of a hack, but... here is a function that will return true if the argument is a function, and false if it's not. It is possible that there are exceptions where this won't work - I look forward to comments.
EDIT - catching the case where the function is in a mex file...
function b = isFunction(fName)
% tries to determine whether the entity called 'fName'
% is a function or a script
% by looking at the file, and seeing if the first line starts with
% the key word "function"
try
w = which(fName);
% test for mex file:
mx = regexp(w, [mexext '$']);
if numel(mx)>0, b = true; return; end
% the correct thing to do... as shown by YYC
% if nargin(fName) >=0, b = true; return; end
% my original alternative:
fid = fopen(w,'r'); % open read only
while(~feof(fid))
l = fgetl(fid);
% strip everything after comment
f = strtok(l, '%');
g = strtok(f, ' ');
if strcmpi(g, 'function'), b=true; break; end
if strlen(g)>0, b=false; break; end
end
fclose(fid);
catch
fprintf(1, '%s not found!\n');
b = false;
end

Matlab function return value

I have one program that has function and the problem, return value, it has too many output.
Like exempley: y = text the answer comes up
Error in text (line 2)
if nargin == 0
Output argument "array" (and maybe others) not assigned during call to "
C:\Users\name\Documents\MATLAB\text.m>text".
The program text.m reads a txt file that contains a couple of names and numbers like
exemple:
John doughlas 15986
Filip duch 357852
and so on. The program convert them to 15986 Doughlas John and so on.
function array = text(~)
if nargin == 0
dirr = '.';
end
answer = dir(dirr);
k=1;
while k <= length(answer)
if answer(k).isdir
answer(k)=[];
else
filename{k}=answer(k).name;
k=k+1;
end
end
chose=menu( 'choose file',filename);
namn = char(filename(chose));
fid = fopen(namn, 'r');
R = textscan(fid,'%s %s %s');
x=-1;
k=0;
while x <= 24
x = k + 1;
All = [R{3}{x},' ',R{1}{x},' ',R{2}{x}];
disp(All)
k = k + 1;
end
fclose(fid);
Is there anyway to fix the problem without starting over from scratch?
Grateful for all the answers!
You specify the function output argument in the definition, but you don't assign anything to it in the function body.
For example, in
function y = student(j)
your output is y. So you have to assign something to y.
Read more about functions in MATLAB.
Here is a working example.
The first part is to create a function called 'functionA' in a filename 'functionA.m'. Then put the following code inside:
function result = functionA(N,alpha)
result = 5;
return
end
The second part is to create another Matlab file(i.e. upto you to name it) or you can use the Matlab command window even. Then run the following code:
getresult = functionA(100,10);
getresult
After running you get the following answer:
ans =
5

Running a MATLAB code fragment without namespace pollution

I'm writing a version of Python's doctest test-runner, for MATLAB (it partly works...). For this to work, I need to run the code in people's examples in their m-file help. I want variables to carry over from one line to the next, e.g.
% >> I = 5 + 33; % expect no output
% >> I
%
% I =
%
% 38
%
To run the tests, I have a loop over matches to the REGEX that searches for the tests. For each match, I evalc the example and make sure the result matches:
for I = 1:length(examples)
try
got = evalc(examples(I).source);
catch exc
got = ['??? ' exc.message];
end
% process the result...
end
The problem is that the example's definition of I has now clobbered the loop variable in my loop, since the assignments carry over from the eval into the outer scope. I looked around for something capable of creating a new scope/workspace, but evalin can only re-use the caller's workspace, which is even worse. I've also considered options with calling a sub-function or save/load, but not gotten anywhere, but maybe I just haven't thought hard enough.
So I guess I need to just name all my variables doctest__system__* and live with the namespace problems... unless you have another idea for a strategy to avoid variable name conflicts?
A very interesting project for sure.. I think the best option you have is to write a separate function to execute the tests, and use a unique prefix for all variable inside of this function to avoid name conflict. Here my attempt at this:
function [PREFIX_b varargout] = testContext(PREFIX_src, PREFIX_srcOutput)
%# TESTCONTEXT Executes the source code and tests for
%# equality against the expected output
%#
%# Input:
%# PREFIX_src - source to execute, cellarry of statements
%# PREFIX_srcOutput - output to expect, cellarray of output of each statement
%#
%# Output:
%# PREFIX_b - true/false for success/failure of test
%# note that the output is strtrim()'ed then strcmp()'ed
%# varargout{1} - variable names assigned in this confined context
%# varargout{2} - variable values assigned
%#
%# Example 1:
%# source = { 'I = 5+33;' 'I' };
%# output = { [], ['I =' char(10) ' 38'] };
%# b = testContext(source, output);
%#
%# Example 2:
%# source = { 'I = 5+33; J = 2;' 'K = 1;' 'disp(I+J+K)' };
%# output = { [], [], '41' };
%# [b varNames varValues] = testContext(source, output);
%#
%# See also: eval evalc
%#
PREFIX_b = true;
try
%# for each statement
for PREFIX_i=1:numel(PREFIX_src)
%# evaluate
PREFIX_output = evalc( PREFIX_src{PREFIX_i} );
PREFIX_output = strtrim(PREFIX_output); %# trim whitespaces
%# compare output
if ~isempty( PREFIX_srcOutput{PREFIX_i} )
if ~strcmp(PREFIX_output,PREFIX_srcOutput{PREFIX_i})
PREFIX_b = false;
return
end
end
end
if nargout > 1
%# list created variables in this context
%#clear ans
PREFIX_vars = whos('-regexp', '^(?!PREFIX_).*'); %# java regex negative lookahead
varargout{1} = { PREFIX_vars.name };
if nargout > 2
%# return those variables
varargout{2} = cell(1,numel(PREFIX_vars));
for PREFIX_i=1:numel(PREFIX_vars)
[~,varargout{2}{PREFIX_i}] = evalc( PREFIX_vars(PREFIX_i).name );
end
end
end
catch ME
warning(ME.identifier, ME.message)
PREFIX_b = false;
varargout{1} = {};
varargout{2} = {};
end
end
I assume you are able to parse the m-file to recover the examples to test, where you have each statement along with its expected output.
As an example, consider this simple test embedded in the header of a function:
I = 5 + 33;
J = 2*I;
disp(I+J)
Since only the last statement has an output, we test it as:
source = {'I = 5 + 33;' 'J = 2*I;' 'disp(I+J)'};
output = {[], [], '114'};
[b varNames varValues] = testContext(source, output)
the results:
b =
1
varNames =
'I' 'J'
varValues =
[38] [76]
It shows whether the test passed of failed. Optionally, the function returns a list of variables created in that context along with their values.