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.
Related
I have a GUI that takes users typed-in equations such as delta_P=C1-C2;velocity=diff(y)./diff(x); all in one string delimited and terminated by ;. Then, within the GUI function, I pull in C1, C2, x, and y in and I want to evaluate to generate delta_P and velocity and assign them into the base Workspace. My problem is I don't know delta_P and velocity ahead of time so that I can't just do:
assignin('base','delta_P',C1-C2);
I need to break down the string to identify the new variable names left of the equal signs and assign to them what are right of the equal signs into the base Workspace?
I condition an input string with one or more statements so that there is no space and no carriage return. Then, I tried the following:
str_in = 'delta_P=C1-C2;velocity=diff(y)./diff(x);'
str_sp = strsplit(str_in,';');
str_sp = str_sp(1:end-1); % last ';' results in an empty char
Then, this is where I get lost:
cellfun(#(c1,c2)assignin('base',c1(1:c2-1),c1(c2+1:end)),str_sp,cellfun(#(c)strfind(c,'='),str_sp,'uni',0),'uni',0);
It just doesn't look efficient
It still doesn't work as c1(c2+1:end) is also a string
I tried eval(c1(1:c2-1)), but MATLAB complains C1,C2,x, and y are undefined.
Thanks.
You should evaluate the expression in the current workspace, and then evaluate the assignment in the base workspace.
Here's an example function which illustrates the logic:
function q61401249
C1 = 1;
C2 = 2;
x = [1 1 2];
y = [2 3 4];
str_in = 'delta_P=C1-C2;velocity=diff(y)./diff(x);';
str_sp = strsplit(str_in,';');
str_sp = str_sp(1:end-1);
for i = 1:length(str_sp)
s = split(str_sp(i),'=');
assignin('base',s{1},eval(s{2}));
end
end
when you run the function, you will see that two new variables have been created in the base workspace, delta_P and velocity as desired.
Of course the assumption here is that the equation is well formed, for instance that there aren't two = signs.
What I would like to be able to do is programmatically change an anonymous function for example by changing all the plus signs to multiplication signs in the function. This example can in many cases this can be done as follows:
function f2 = changefunction(f1)
fs = func2str(f1);
fs(fs=='+') = '*';
f2 = str2func(fs);
end
But consider the example
f = #(x) x+5;
a = 5;
g = #(x) x+a;
Both f and g will be anonymous functions that adds 5 to whatever you plug into it; however only f will be changed correctly by the changefunction function, whereas g will be changed into a function that will err on any input.
So my question is is it possible to extract the workspace from the function handle and retain it in the new function handle created? I need to do it programmatically and preferably without using the built-in function functions!
One naive implementation is to replace str2func with eval so you are not running into str2func's roadblock of not allowing access to local variables. We can use functions to obtain the workspace information for the input function handle.
For example:
a = 5;
f = #(x) x+a;
finfo = functions(f)
Yields:
finfo =
struct with fields:
function: '#(x)x+a'
type: 'anonymous'
file: 'X:\testcode-matlab\testcode.m'
workspace: {[1×1 struct]}
within_file_path: 'testcode'
Where workspace is a cell array containing a structure (come on MathWorks...) containing all of the variables in your function handle's namespace:
>> wspace = finfo.workspace{1}
wspace =
struct with fields:
a: 5
Using this functionality, the naive solution is to loop through the variables in this workspace, assign them in the namespace of changefunction, then use eval to generate the new function handle.
For example:
function f2 = changefunction_new(f1)
tmp = functions(f1);
workspacevars = tmp.workspace{1};
varnames = fieldnames(workspacevars);
for ii = 1:length(varnames)
evalstr = sprintf('%s = %d;', varnames{ii}, workspacevars.(varnames{ii}));
eval(evalstr);
end
fs = func2str(f1);
fs(fs=='+') = '*';
f2 = eval(fs);
end
Here I'm assuming that the variables are going to be strictly numeric. You can add logic to check the class of the data to be generated if this is not always the case.
With this we have:
a = 5;
g = #(x) x+a;
test1 = changefunction(g);
test2 = changefunction_new(g);
>> g(1)
ans =
6
>> test1(1)
Undefined function or variable 'a'.
Error in testcode>#(x)x*a
>> test2(1)
ans =
5
All that being said, the best solution really is to just explicitly define your function handles. It may be a pain but it's much easier to understand and debug.
A few caveats:
Because eval arbitrarily executes all code passed to it, it can be a very dangerous function that must be used with care.
The documentation for functions warns against using it programmatically, so take care to check behavior as MATLAB versions change:
Use the functions function for querying and debugging purposes only.
Note: Do not use functions programmatically because its behavior could change in subsequent MATLAB® releases.
One possible way to do this is to save the function handle to a .mat file (using the -v7.3 flag so that it creates an easily-modifiable HDF5 file), modify the struct within the file that contains the workspace data for the anonymous function (using the HDF5 tools built into MATLAB), and then load the anonymous function again from the file.
Here is a little function which does exactly that (and it works for relatively simple variable types)
function result = modifyfunc(f, varname, value)
% modifyfunc - Modify the workspace of an anonymous function
%
% INPUTS:
% f: Function Handle, Anonymous function to modify
% varname: String, Name of the variable to modify
% value: Data to replace the specified variable
% If the value is a struct, recursively modify the function handle
if isstruct(value)
fields = fieldnames(value);
result = f;
% Modify each field separately
for k = 1:numel(fields)
% Append the fieldname to the variable name and modify
name = [varname, '.', fields{k}];
result = modifyfunc(result, name, value.(fields{k}));
end
return;
end
% Write the anonymous function to an HDF5 file
fname = tempname;
save(fname, 'f', '-mat', '-v7.3');
% Replace any "." in the variable name with "/" to construct the HDF5 path
varname = strrep(varname, '.' , '/');
% Now modify the data in the file
h5write(fname, ['/#refs#/e/' varname], value);
% Load the modified function handle from the file
result = load(fname, '-mat');
result = result.f;
% Remove the temporary file
delete(fname);
end
And you can use it like:
a = 1;
b = struct('field', 2);
f = #(x)disp(a + b.field + x);
f(10)
% 13
f2 = modifyfunc(f, 'a', 2);
f2(10)
% 14
f3 = modifyfunc(f2, 'b.field', 3);
f3(10)
% 15
b.field = 4;
f4 = modifyfunc(f3, 'b', b);
f4(10)
% 16
Some caveats include:
The replacement data must be the same size as the original data
This relies upon the format of the .mat file which for anonymous functions is completely undocumented so it could fail in future releases.
This currently doesn't work for variables in the function workspace that are cell arrays.
I have a strange error. The following is a Minimal, Complete, and Verifiable example of my problem:
ba = 1;
bb = 2;
bc = 3;
whos
% Get variable names
varnames = who('b*')
% Definition of the filename
filename = 'bVariables';
cellfun(#(x) display( x ), varnames);
cellfun(#(x) save( filename, x, '-append' ), varnames);
First I declare variables starting with letter b. Then I use the who command to get the variable names that start with letter b (assume that I don't know them before running the script). The names are returned as a cell array of strings (char vectors).
I then try to save each variable individually to the given filename with save defined as an anonymous function used by cellfun.
Here is the output I get from the above MCV:
varnames =
'ba'
'bb'
'bc'
x =
ba
x =
bb
x =
bc
Error using save
Variable 'ba' not found.
Error in #(x)save(filename,x,'-append')
This is really strange as the variables really do exist in workspace.
Am I using cellfun wrong?
Is it probably related to the use of an anonymous function and the visibility of variables?
It seems that it is because of the anonymous function and the scope of the variables. I can reproduce the error simply with eval function:
ba = 1;
bb = 2;
bc = 3;
whos
% Get variable names
varnames = who('b*')
cellfun(#(x) eval( x ), varnames);
However you can simply do it by doing this:
save(filename,varnames{:})
This outputs the content of the cell as a comma separated list handled by the save function. You can also add the '-append' at the end to make it append to that file: save(filename,varnames{:},'-append')
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.
I have a simple but interesting question. i tired hard to google it but my google got upset and giving me the same results...
i wanted to know is it possible to Update a constant variable form workspace command..
A Simple Example:
function y =StupidQuestion
a = 10; % some value
b =[5,6,7;1,2,8]; % some value
y = b*a % some operation
I forget to tell you that we can do it with simulink block by using below command
set_param('obj', 'parameter1', value1, 'parameter2', value2, ...)
i Want to use the assigned value for 3 weeks and without any reason i wants to change my values [a,b] to other but through command windows. any Idea. Waiting for your interesting Reply...................
You can set defaults for the inputs:
function y = foo(a,b)
if nargin < 1 || isempty(a), a = 10; end
if nargin < 2 || isempty(b), b = [5,6,7;1,2,8]; end
y = b*a
end
You can call foo() without inputs (and it will use the defaults for a and b) or supply your own values as: foo(12), foo(12,[10,20]), foo([],[23,23]), etc...
A possible way is to save some variables in an external file. Note that in this case a and b are only in the function workspace (you won't see their values unless you load the contents of test.mat separately). I'm passing the filename in rather than hard-coding it in case you need to switch between multiple settings.
Personally I would prefer to have a human-readable data file, but the concept remains the same (you'd just need some parser function which returned values for a and b given a file).
a = 10; % some value
b =[5,6,7;1,2,8]; % some value
save('test.mat','a','b');
clear a b;
function y = savedvariables(filename)
load(filename);
y = b*a; % some operation
end
y = savedvariables('test.mat');