除了解析函数文件之外,有没有办法在matlab中获取函数的输入和输出参数的名称?
例如,给定以下函数文件:
divide.m
function [value, remain] = divide(left, right)
value = floor(left / right);
remain = left / right - value;
end
从函数外部,我想得到一个输出参数数组,在这里:['value', 'remain']
,输入参数类似:['left', 'right']
。
在matlab中有一种简单的方法吗? Matlab通常似乎很好地支持反射。
编辑背景:
这样做的目的是在窗口中显示功能参数供用户输入。我正在编写一种信号处理程序,对这些信号执行操作的函数存储在子文件夹中。我已经有一个列表和用户可以选择的每个函数的名称,但是某些函数需要额外的参数(例如,平滑函数可能会将窗口大小作为参数)。
目前,我可以在程序将找到的子文件夹中添加一个新功能,用户可以选择它来执行操作。我缺少的是用户指定输入和输出参数,这里我遇到了障碍,因为我找不到函数的名称。
答案 0 :(得分:10)
如果您的问题仅限于您要解析文件中主要功能的function declaration line的简单情况(即您不会处理local functions,nested functions或者anonymous functions),然后您可以使用一些标准字符串操作和regular expressions提取输入和输出参数名称,因为它们出现在文件中。函数声明行具有标准格式,但由于以下原因,您必须考虑一些变体:
(事实证明,块评论的核算是最棘手的部分......)
我已经整理了一个能够处理以上所有内容的函数get_arg_names
。如果给它一个函数文件的路径,它将返回两个包含输入和输出参数字符串的单元格数组(如果没有,则返回空单元格数组)。请注意,具有可变输入或输出列表的函数将分别仅为变量名称列出'varargin'
或'varargout'
。这是功能:
function [inputNames, outputNames] = get_arg_names(filePath)
% Open the file:
fid = fopen(filePath);
% Skip leading comments and empty lines:
defLine = '';
while all(isspace(defLine))
defLine = strip_comments(fgets(fid));
end
% Collect all lines if the definition is on multiple lines:
index = strfind(defLine, '...');
while ~isempty(index)
defLine = [defLine(1:index-1) strip_comments(fgets(fid))];
index = strfind(defLine, '...');
end
% Close the file:
fclose(fid);
% Create the regular expression to match:
matchStr = '\s*function\s+';
if any(defLine == '=')
matchStr = strcat(matchStr, '\[?(?<outArgs>[\w, ]*)\]?\s*=\s*');
end
matchStr = strcat(matchStr, '\w+\s*\(?(?<inArgs>[\w, ]*)\)?');
% Parse the definition line (case insensitive):
argStruct = regexpi(defLine, matchStr, 'names');
% Format the input argument names:
if isfield(argStruct, 'inArgs') && ~isempty(argStruct.inArgs)
inputNames = strtrim(textscan(argStruct.inArgs, '%s', ...
'Delimiter', ','));
else
inputNames = {};
end
% Format the output argument names:
if isfield(argStruct, 'outArgs') && ~isempty(argStruct.outArgs)
outputNames = strtrim(textscan(argStruct.outArgs, '%s', ...
'Delimiter', ','));
else
outputNames = {};
end
% Nested functions:
function str = strip_comments(str)
if strcmp(strtrim(str), '%{')
strip_comment_block;
str = strip_comments(fgets(fid));
else
str = strtok([' ' str], '%');
end
end
function strip_comment_block
str = strtrim(fgets(fid));
while ~strcmp(str, '%}')
if strcmp(str, '%{')
strip_comment_block;
end
str = strtrim(fgets(fid));
end
end
end
答案 1 :(得分:10)
MATLAB提供了一种获取类元数据信息的方法(使用meta
包),但这仅适用于OOP类而非常规函数。
一个技巧是动态编写一个类定义,其中包含您想要处理的函数的源代码,并让MATLAB处理源代码的解析(这可能很棘手,因为您&#39; d想象一下:函数定义行跨越多行,实际定义之前的注释等等......)
因此,在您的案例中创建的临时文件将如下所示:
classdef SomeTempClassName
methods
function [value, remain] = divide(left, right)
%# ...
end
end
end
然后可以传递给meta.class.fromName
来解析元数据......
这是一个快速而又脏的黑客实现:
function [inputNames,outputNames] = getArgNames(functionFile)
%# get some random file name
fname = tempname;
[~,fname] = fileparts(fname);
%# read input function content as string
str = fileread(which(functionFile));
%# build a class containing that function source, and write it to file
fid = fopen([fname '.m'], 'w');
fprintf(fid, 'classdef %s; methods;\n %s\n end; end', fname, str);
fclose(fid);
%# terminating function definition with an end statement is not
%# always required, but now becomes required with classdef
missingEndErrMsg = 'An END might be missing, possibly matching CLASSDEF.';
c = checkcode([fname '.m']); %# run mlint code analyzer on file
if ismember(missingEndErrMsg,{c.message})
% append "end" keyword to class file
str = fileread([fname '.m']);
fid = fopen([fname '.m'], 'w');
fprintf(fid, '%s \n end', str);
fclose(fid);
end
%# refresh path to force MATLAB to detect new class
rehash
%# introspection (deal with cases of nested/sub-function)
m = meta.class.fromName(fname);
idx = find(ismember({m.MethodList.Name},functionFile));
inputNames = m.MethodList(idx).InputNames;
outputNames = m.MethodList(idx).OutputNames;
%# delete temp file when done
delete([fname '.m'])
end
并简单地运行:
>> [in,out] = getArgNames('divide')
in =
'left'
'right'
out =
'value'
'remain'
答案 2 :(得分:3)
对于一般功能(想想像varargin等等),这将是非常困难的(阅读:不可能)。此外,通常,依赖变量名称作为文档形式可能......不是你想要的。我将提出一个不同的方法。
由于您控制程序,不仅要使用m文件指定每个模块,还要使用包含额外信息的表条目。你可以记录额外的参数,函数本身,当选项是布尔值时表示,并将它们显示为复选框等。
现在,把它放在哪里?我建议让主m-file函数返回结构,作为一个模块加载步骤,带有一个函数句柄,指向执行实际工作的子函数(或嵌套函数)。这保留了我确定要保留的单文件设置,并为您的模块提供了更加可配置的设置。
function module = divide_load()
module.fn = @my_divide;
module.name = 'Divide';
module.description = 'Divide two signals';
module.param(1).name = 'left';
module.param(1).description = 'left signal';
module.param(1).required_shape = 'columnvector';
% Etc, etc.
function [value, remain] = my_divide(left, right)
value = floor(left / right);
remain = left / right - value;
end
end
答案 3 :(得分:1)
如果您无法从编程语言中获取有关其内容的信息(例如“反射”),则必须跳出该语言。
另一张海报建议使用“正则表达式”,它在应用于解析真实程序时总是失败,因为正则表达式无法解析无上下文的语言。
为了可靠地执行此操作,您需要一个真正的M语言解析器,它将允许您访问解析树。那么这很容易。
我们的DMS Software Reengineering Toolkit有一个可用的M语言解析器,可以这样做。
答案 4 :(得分:0)
您是否考虑过使用map containers?
您可以沿着这些方向编写功能。 。 。
function [outMAP] = divide(inMAP)
outMAP = containers.Map();
outMAP('value') = floor(inMAP('left') / inMAP('right'));
outMAP('remain') = inMAP('left') / inMAP('right') - outMAP('value');
end
......然后像这样打电话给他们......
inMAP = containers.Map({'left', 'right'}, {4, 5});
outMAP = divide(inMAP);
...然后使用以下语法简单地检查变量名称......
>> keys(inMAP)
ans =
'left' 'right'
答案 5 :(得分:-3)
inputname(argnum)
http://www.mathworks.com/help/techdoc/ref/inputname.html。