Подтвердить что ты не робот

Как получить имена параметров функции в 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 обычно, кажется, поддерживает отражение довольно хорошо.

ИЗМЕНИТЬ Фон:

Цель этого - представить параметры функции в окне для входа пользователя. Я пишу какую-то программу обработки сигналов, а функции для выполнения этих сигналов хранятся во вложенной папке. У меня уже есть список и имена каждой функции, из которой пользователь может выбрать, но некоторые функции требуют дополнительных аргументов (например, гладкая функция может принимать размер окна в качестве параметра).

В настоящий момент я могу добавить новую функцию в подпапку, которую программа найдет, и пользователь может выбрать ее для выполнения операции. То, что мне не хватает, - это указать пользователю параметры ввода и вывода, и здесь я столкнулся с барьером здесь, потому что не могу найти имена функций.

4b9b3361

Ответ 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'