How do I retrieve the names of function parameters in matlab?

前端 未结 6 1223
面向向阳花
面向向阳花 2020-12-01 12:18

Aside from parsing the function file, is there a way to get the names of the input and output arguments to a function in matlab?

For example, given the following fun

相关标签:
6条回答
  • 2020-12-01 13:00

    This is going to be very hard (read: impossible) to do for general functions (think of things like varargin, etc). Also, in general, relying on variable names as a form of documentation might be... not what you want. I'm going to suggest a different approach.

    Since you control the program, what about specifying each module not just with the m-file, but also with a table entry with extra information. You could document the extra parameters, the function itself, notate when options are booleans and present them as checkboxes, etc.

    Now, where to put this? I would suggest to have the main m-file function return the structure, as sort of a module loading step, with a function handle that points to the subfunction (or nested function) that does the real work. This preserves the single-file setup that I'm sure you want to keep, and makes for a much more configurable setup for your modules.

    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
    
    0 讨论(0)
  • 2020-12-01 13:01

    inputname(argnum) http://www.mathworks.com/help/techdoc/ref/inputname.html .

    0 讨论(0)
  • 2020-12-01 13:13

    Have you considered using map containers?

    You can write your functions along these lines . . .

    function [outMAP] = divide(inMAP)
         outMAP = containers.Map();
         outMAP('value') = floor(inMAP('left') / inMAP('right'));
         outMAP('remain') = inMAP('left') / inMAP('right') - outMAP('value');
    end
    

    ...and call them like this ...

    inMAP  = containers.Map({'left', 'right'}, {4, 5});
    outMAP = divide(inMAP);
    

    ...and then simply examine tha variable names using the following syntax...

    >> keys(inMAP)
    
    ans = 
    
        'left'    'right'
    
    0 讨论(0)
  • 2020-12-01 13:16

    If your problem is limited to the simple case where you want to parse the function declaration line of a primary function in a file (i.e. you won't be dealing with local functions, nested functions, or anonymous functions), then you can extract the input and output argument names as they appear in the file using some standard string operations and regular expressions. The function declaration line has a standard format, but you have to account for a few variations due to:

    • Varying amounts of white space or blank lines,
    • The presence of single-line or block comments, and
    • Having the declaration broken up on more than one line.

    (It turns out that accounting for a block comment was the trickiest part...)

    I've put together a function get_arg_names that will handle all the above. If you give it a path to the function file, it will return two cell arrays containing your input and output parameter strings (or empty cell arrays if there are none). Note that functions with variable input or output lists will simply list 'varargin' or 'varargout', respectively, for the variable names. Here's the function:

    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
    
    0 讨论(0)
  • 2020-12-01 13:18

    MATLAB offers a way to get information about class metadata (using the meta package), however this is only available for OOP classes not regular functions.

    One trick is to write a class definition on the fly, which contain the source of the function you would like to process, and let MATLAB deal with the parsing of the source code (which can be tricky as you'd imagine: function definition line spans multiple lines, comments before the actual definition, etc...)

    So the temporary file created in your case would look like:

    classdef SomeTempClassName
        methods
            function [value, remain] = divide(left, right)
                %# ...
            end
        end
    end
    

    which can be then passed to meta.class.fromName to parse for metadata...


    Here is a quick-and-dirty implementation of this hack:

    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
    

    and simply run as:

    >> [in,out] = getArgNames('divide')
    in = 
        'left'
        'right'
    out = 
        'value'
        'remain'
    
    0 讨论(0)
  • 2020-12-01 13:19

    When you can't get information from a programming langauge about its contents (e.g., "reflection"), you have to step outside the language.

    Another poster suggested "regular expressions", which always fail when applied to parsing real programs because regexps cannot parse context free langauges.

    To do this reliably, you need a real M language parser, that will give you access to the parse tree. Then this is fairly easy.

    Our DMS Software Reengineering Toolkit has an M language parser available for it, and could do this.

    0 讨论(0)
提交回复
热议问题