Reputation: 46795
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:
Upvotes: 4
Views: 216
Reputation: 30046
In this answer I list 3 subtly different versions of the function describe
.
fpritnf
disp
describe('myvar{1}')
.You can use various standard functions to get the info you want:
varargin
to accept variable number of input variablesdbstack
to get the file name / current lineinputname
to get the names of inputs as they are passed into describe
fprintf
to display with new line charactersvarargout
to optionally return or display the resultSo 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.
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
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, :)
Upvotes: 5
Reputation: 22225
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.
Upvotes: 1
Reputation: 181
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.
Upvotes: 0