Get name of code section in Matlab script - matlab

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.

Related

Matlab - Do action after every line

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.

Determine the most recently evaluated variable and save it to .mat-file

Is there a way to find the most recently evaluated variable in Matlab? who or whos don't seem to time stamp the variables in the workspace.
Here is a use case. I would like to have a generic function 'probe()' that can be called anywhere in a Matlab script/code. I would like to save the most recently evaluated variable to a .mat-file without having to pass any custom parameter relating to the variable being saved. Is this possible?
ans comes close to what I am trying to achieve, but it will not be available as my code has variables on the left hand side in the assignments.
If you are doing this in the Command Prompt, what you can do is use this post by gnovice to retrieve the entire command history since you opened MATLAB to a text array. Once you do that, you simply search the second last row for the variable before the equals sign.... assuming you did a lhs statement. You also want to take into account that you are echoing the variable in the Command Prompt without a left-hand side statement. We can easily find this through regexp.
You need to search at the second last row of the text array because gnovice's code to capture the history requires an additional line of code. This code gets captured in the history and that's not what you want. As such, you need to look at the second last row / entry.
Therefore, do this:
history = com.mathworks.mlservices.MLCommandHistoryServices.getSessionHistory; %// From gnovice's post
historyText = char(history);
lne = historyText(end-1,:); %// Get second last line
%// Do a regexp and retrieve the text with or without the equals sign
var = regexp(lne, '\w*', 'match');
%// Get first match which is the variable before any symbols or just the word
var = var{1};
Here's a quick example. This was my complete command history before trying the above code:
>> A = rand(10,10);
>> B = A + 2;
>> D = B * 3;
After I run the above code, I get for var:
var =
D
Similarly, if we just evaluate the answer without assigning anything to the left-hand side:
>> A = rand(10,10);
>> B = A + 3;
>> A
Running the above code, I get:
var =
A
To finally end it all, if you want to save this variable to disk, you can use an eval statement to facilitate this:
>> name_of_file = 'file.mat';
>> eval(['save ' name_of_file ' ' var]);
The above code will take a file name that you specify... so in this case it'll be test.mat, then invoke the save command with var as the variable from the Workspace you would like to save.
This is a basic sketch how you can do it, using the function dbstack:
function probe
%// get filename of caller file and line where probe was called
lineANDfile = dbstack;
file = lineANDfile(end).file;
linenum = lineANDfile(end).line;
%// read caller m-file
mLines = textread(file, '%s','delimiter', '\n');
%// get line previous of the line where probe was called
mLinesUntilProbeCall = mLines(1:linenum - 1);
%// last non empty line before probe call -> line of interest
mLine = mLines{ find( ~cellfun(#isempty,mLinesUntilProbeCall),1,'last') };
%// get string (variable name) left of =
varnameCell = textscan(mLine,'%s');
%// isolate varnames
getFouts = regexp(varnameCell{1}{1},'(?<=\[).+?(?=\])','match');
if isempty(getFouts)
varnames = varnameCell{1}(1);
else
varnames = strsplit( getFouts{1},',');
end
%// create struct with varnames as fields
for ii= 1:numel(varnames)
probeOut.(varnames{ii}) = evalin('base',varnames{ii});
end
save('probeOut.mat','probeOut');
end
which you can call in a script like:
y = 5;
xz = 42;
probe %// empty lines before call allowed!
it will create a .mat-file with a struct probeOutand the field xz.
After loading the .mat-file again:
>> probeOut.xz
ans =
42
It will also work, if you have multiple output arguments:
y = 5;
[xz,z] = deal(42,5);
probe
your .mat-file will then look like:
The case
y = 5;
[xz] = deal(42,5);
probe
is also covered.

Parsing a data file in matlab

I have a text file with two columns of data. I want to split this file and save it as two individual strings in matlab, but I also need to stop copying the data when I meet an identifier in the data then stat two new strings.
For example
H 3
7 F
B B
T Y
SPLIT
<>
Where SPLIT <> is where I want to end the current string.
I'm trying to use fopen and fscanf, but struggling to get it to do what I want it to.
I tried the following script on the example you provided and it works. I believe the comments are very self explanatory.
% Open text file.
fid = fopen('test.txt');
% Read the first line.
tline = fgetl(fid);
% Initialize counter.
ii = 1;
% Check for end string.
while ~strcmp(tline, 'SPLIT')
% Analyze line only if it is not an empty one.
if ~strcmp(tline, '')
% Read the current line and split it into column 1 and column 2.
[column1(ii), column2(ii)] = strread(tline, ['%c %c']);
% Advance counter.
ii = ii + 1;
end
% Read the next line.
tline = fgetl(fid);
end
% Display results in console.
column1
column2
% Close text file.
fclose(fid);
The key functions here are fgetl and strread. Take a look at their documentation, it has some very nice examples as well. Hope it helps.

Making a matrix of strings read in from file using feof function in matlab

I have an index file (called runnumber_odour.txt) that looks like this:
run00001.txt ptol
run00002.txt cdeg
run00003.txt adef
run00004.txt adfg
I need some way of loading this in to a matrix in matlab, such that I can search through the second column to find one of those strings, load the corresponding file and do some data analysis with it. (i.e. if I search for "ptol", it should load run00001.txt and analyse the data in that file).
I've tried this:
clear; clc ;
% load index file - runnumber_odour.txt
runnumber_odour = fopen('Runnumber_odour.txt','r');
count = 1;
lines2skip = 0;
while ~feof(runnumber_odour)
runnumber_odourmat = zeros(817,2);
if count <= lines2skip
count = count+1;
[~] = fgets(runnumber_odour); % throw away unwanted line
continue;
else
line = strcat(fgets(runnumber_odour));
runnumber_odourmat = [runnumber_odourmat ;cell2mat(textscan(line, '%f')).'];
count = count +1;
end
end
runnumber_odourmat
But that just produces a 817 by 2 matrix of zeros (i.e. not writing to the matrix), but without the line runnumber_odourmat = zeros(817,2); I get the error "undefined function or variable 'runnumber_odourmat'.
I have also tried this with strtrim instead of strcat but that also doesn't work, with the same problem.
So, how do I load that file in to a matrix in matlab?
You can do all of this pretty easily using a Map object so you will not have to do any searching or anything like that. Your second column will be a key to the first column. The code will be as follows
clc; close all; clear all;
fid = fopen('fileList.txt','r'); %# open file for reading
count = 1;
content = {};
lines2skip = 0;
fileMap = containers.Map();
while ~feof(fid)
if count <= lines2skip
count = count+1;
[~] = fgets(fid); % throw away unwanted line
else
line = strtrim(fgets(fid));
parts = regexp(line,' ','split');
if numel(parts) >= 2
fileMap(parts{2}) = parts{1};
end
count = count +1;
end
end
fclose(fid);
fileName = fileMap('ptol')
% do what you need to do with this filename
This will provide for quick access to any element
You can then do what was described in the previous question you had asked, with the answer I provided.

How to remove line from m file in MATLAB after its execution

I'm stuck in one case related to matlab.
I create a file through script by using these commands:
delete output
delete f.m
clc;
% This is a Cell Array!
prompt={'Enter Function f(x) Here :'};...
% Name of the Dialog Box
name='Trapezoidal Rule Input Screen';...
numlines=1; % Number of lines visible for User Input
% Default Answer
defaultanswer={'1 + exp(-x).*sin(4*x)'};...
% Creating the Dialog box where the User Input is stored into a Cell Array
answer=inputdlg(prompt,name,numlines,defaultanswer);...
global y1
y1=answer{1};...
diary f.m;
disp('function y = f(x)');...
%disp('y = '),disp(y),disp(';');...
%disp('y = ((1+(x^2))^-1);');...
fprintf('y=%s;\n',y1);
diary off;
f(0);
when the click on RUN CHECK button then f.m file had been created because it is part of script and generate the following code in f.m file
function y = f(x)
%disp('y = '),disp(y),disp(';');...
%disp('y = ((1+(x^2))^-1);');...
fprintf('y=%s;\n',y1);
y=1 + exp(-x).*sin(4*x);
diary off;
but it stop and highlight error that y1 is apparently use before it is define.
fprintf('y=%s;\n',y1);
so if any 1 have some solution regarding this problem please help me out.
I would not abuse the diary command for this.
Why not just:
fid = fopen('f.m','w');
fprintf(fid,'function y=f(x)\n');
fprintf(fid,'y=%s;\n';
fclose(fid);