Как получить имена параметров функции в Matlab?
Помимо анализа файла функций, существует ли способ получить имена входных и выходных аргументов функции в matlab?
Например, при использовании следующего файла функции:
divide.m
function [value, remain] = divide(left, right)
value = floor(left / right);
remain = left / right - value;
end
Извне функции я хочу получить массив выходных аргументов: ['value', 'remain']
и аналогично для входных аргументов: ['left', 'right']
.
Есть ли простой способ сделать это в Matlab? Matlab обычно, кажется, поддерживает отражение довольно хорошо.
ИЗМЕНИТЬ Фон:
Цель этого - представить параметры функции в окне для входа пользователя. Я пишу какую-то программу обработки сигналов, а функции для выполнения этих сигналов хранятся во вложенной папке. У меня уже есть список и имена каждой функции, из которой пользователь может выбрать, но некоторые функции требуют дополнительных аргументов (например, гладкая функция может принимать размер окна в качестве параметра).
В настоящий момент я могу добавить новую функцию в подпапку, которую программа найдет, и пользователь может выбрать ее для выполнения операции. То, что мне не хватает, - это указать пользователю параметры ввода и вывода, и здесь я столкнулся с барьером здесь, потому что не могу найти имена функций.
Ответы
Ответ 1
Если ваша проблема ограничена простым случаем, когда вы хотите проанализировать строку объявления функции первичной функции в файле (т.е. вы не будете иметь дело с локальными функциями, вложенными функциями или анонимными функциями), то вы можете извлечь имена входных и выходных аргументов в том виде, в каком они появляются в файле, используя некоторые стандартные строковые операции и регулярные выражения. Строка объявления функции имеет стандартный формат, но вам нужно учесть несколько вариантов из-за:
(Оказывается, учет блочных комментариев был самой сложной частью...)
Я собрал функцию 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
Ответ 2
MATLAB предлагает способ получения информации о метаданных класса (с использованием пакета meta
), однако это доступно только для классов ООП, которые не являются регулярными функциями.
Один трюк заключается в том, чтобы написать определение класса "на лету", в котором содержится источник функции, которую вы хотите обработать, и пусть MATLAB справится с разбором исходного кода (что может показаться сложным, как вы себе представляете: строка определения функции охватывает несколько строк, комментарии до фактического определения и т.д.)
Таким образом, временный файл, созданный в вашем случае, будет выглядеть так:
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'
Ответ 3
Это будет очень сложно (читать: невозможно) для общих функций (подумайте о таких вещах, как varargin и т.д.). Кроме того, в целом, опираясь на имена переменных в качестве формы документации, может быть... не то, что вы хотите. Я собираюсь предложить другой подход.
Так как вы управляете программой, то о том, как указывать каждый модуль не только с m файлом, но также с записью таблицы с дополнительной информацией. Вы можете документировать дополнительные параметры, сама функция, обозначать, когда опции являются логическими, и представлять их как флажки и т.д.
Теперь, где это поставить? Я хотел бы предложить, чтобы основная функция m файла возвращала структуру, как своего рода шаг загрузки модуля, с дескриптором функции, который указывает на подфункцию (или вложенную функцию), которая выполняет реальную работу. Это сохраняет однофайльную настройку, которую я уверен, что вы хотите сохранить, и делает более настраиваемую настройку для ваших модулей.
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
Ответ 4
Если вы не можете получить информацию из программы langauge о ее содержимом (например, "отражение" ), вы должны выйти за пределы языка.
Другой плакат предлагает "регулярные выражения", которые всегда терпят неудачу при применении к синтаксическому анализу реальных программ, поскольку регулярные выражения не могут анализировать контекстно-свободные langauges.
Чтобы сделать это надежно, вам нужен настоящий синтаксический анализатор языка M, который даст вам доступ к дереву разбора. Тогда это довольно легко.
Наш DMS Software Reengineering Toolkit имеет доступный для него анализатор языка M и может сделать это.
Ответ 5
Считаете ли вы использование картографических контейнеров?
Вы можете написать свои функции в этих строках.,
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'
Ответ 6
inputname(argnum)
http://www.mathworks.com/help/techdoc/ref/inputname.html.