打印当前代码行和特定变量的名称、类型和维度

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

要调试我的 Octave/MATLAB 代码,我希望能够执行如下操作:

A = magic(3);
b = 42;
describe(@A, @b);

并得到如下输出:

filename.m line 3: "A" is a 3x3 matrix
filename.m line 3: "b" is a scalar of value: 42

对于多个变量,如何打印:

您可以使用 size 来确定变量的大小,类型使用 class,名称使用 inputname。 例如:

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

我不知道如何使用文件名和行,如果你想用另一种方式显示它,你可以使用 if 条件将标量与向量与矩阵分开。

概述

在这个回答中,我列出了函数 describe.

的 3 个细微不同的版本
  1. 获取任意数量的变量并创建输出字符串并使用 fpritnf
  2. 显示它
  3. 获取任意数量的变量并创建输出 元胞数组 并使用 disp
  4. 显示它
  5. 采用任意数量的变量 names 并创建一个输出字符串,如 1 中所示。这具有能够处理索引的优点,如 describe('myvar{1}').

主要功能描述和版本 1。

您可以使用各种标准函数来获取您想要的信息:

  • varargin接受可变数量的输入变量
  • dbstack获取文件名/当前行
  • inputname 获取传递给 describe
  • 的输入的名称
  • fprintf 换行符显示
  • varargout 可选择 return 或显示结果

所以像这样创建你的 describe 函数:

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 

这里唯一需要的调整实际上是格式化,您需要的所有功能都在这里,希望内置足够的灵活性来满足您的需求。


示例输出:

% 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]

在 Matlab R2015b 中测试,根据文档,上面列出的所有函数自 R2006a 之前就已经存在,所以我认为它们很可能具有 Octave 等价物。


版本 2。

单元格而不是带有行分隔符的字符串。

这有一个不那么漂亮的输出,但也可能是一个不那么笨拙的方法,将字符串分配给一个元胞数组,而不是必须依赖 fprintf 换行。使用以下(为简洁起见未注释)版本可以轻松完成。

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 

版本 3。

将变量 names 作为字符串传递,因此显示 'myvar(1)' 之类的内容。

这使用 evalin 来评估 caller 工作区中的变量(从中调用 describe)。注意:当您在此 describe 函数中重新创建变量以获取其属性时,这可能会占用更多内存。

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 

使用示例:

% 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, :)

我自己也用类似的东西。这是我的:

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

编辑: 我知道这不是对您所问问题的字面答案,但我一直在使用它并认为它可能对分享有用。 :)


PS。话虽如此,我从来没有想过我可能想以非交互方式使用这样的函数:如果我需要以这种方式检查变量,我只需放置一个断点(或 keyboard 指令) 在代码中然后检查终端中最相关的内容,因此我从未想过要手动报告文件名和行号!您需要像这样执行非交互式调试的用例是什么?如果它是为了 post-mortem "testing" 目的,那么无论如何你真的应该编写适当的测试和完整性检查!

此外,这仅适用于单个变量,但我觉得更可取;如果你想要更多,这是一个非常简单的单行循环。