How to deal with name/value pairs of function arguments in MATLAB

后端 未结 14 1103
忘掉有多难
忘掉有多难 2020-11-28 19:28

I have a function that takes optional arguments as name/value pairs.

function example(varargin)
% Lots of set up stuff
vargs = varargin;
nargs = length(vargs         


        
14条回答
  •  旧时难觅i
    2020-11-28 19:48

    I have made a function based on Jonas and Richie Cotton. It implements both functionalities (flexible arguments or restricted, meaning that only variables existing in the defaults are allowed), and a few other things like syntactic sugar and sanity checks.

    function argStruct = getnargs(varargin, defaults, restrict_flag)
    %GETNARGS Converts name/value pairs to a struct (this allows to process named optional arguments).
    % 
    % ARGSTRUCT = GETNARGS(VARARGIN, DEFAULTS, restrict_flag) converts
    % name/value pairs to a struct, with defaults.  The function expects an
    % even number of arguments in VARARGIN, alternating NAME then VALUE.
    % (Each NAME should be a valid variable name and is case sensitive.)
    % Also VARARGIN should be a cell, and defaults should be a struct().
    % Optionally: you can set restrict_flag to true if you want that only arguments names specified in defaults be allowed. Also, if restrict_flag = 2, arguments that aren't in the defaults will just be ignored.
    % After calling this function, you can access your arguments using: argstruct.your_argument_name
    %
    % Examples: 
    %
    % No defaults
    % getnargs( {'foo', 123, 'bar', 'qwerty'} )
    %
    % With defaults
    % getnargs( {'foo', 123, 'bar', 'qwerty'} , ...
    %               struct('foo', 987, 'bar', magic(3)) )
    %
    % See also: inputParser
    %
    % Authors: Jonas, Richie Cotton and LRQ3000
    %
    
        % Extract the arguments if it's inside a sub-struct (happens on Octave), because anyway it's impossible that the number of argument be 1 (you need at least a couple, thus two)
        if (numel(varargin) == 1)
            varargin = varargin{:};
        end
    
        % Sanity check: we need a multiple of couples, if we get an odd number of arguments then that's wrong (probably missing a value somewhere)
        nArgs = length(varargin);
        if rem(nArgs, 2) ~= 0
            error('NameValuePairToStruct:NotNameValuePairs', ...
                'Inputs were not name/value pairs');
        end
    
        % Sanity check: if defaults is not supplied, it's by default an empty struct
        if ~exist('defaults', 'var')
            defaults = struct;
        end
        if ~exist('restrict_flag', 'var')
            restrict_flag = false;
        end
    
        % Syntactic sugar: if defaults is also a cell instead of a struct, we convert it on-the-fly
        if iscell(defaults)
            defaults = struct(defaults{:});
        end
    
        optionNames = fieldnames(defaults); % extract all default arguments names (useful for restrict_flag)
    
        argStruct = defaults; % copy over the defaults: by default, all arguments will have the default value.After we will simply overwrite the defaults with the user specified values.
        for i = 1:2:nArgs % iterate over couples of argument/value
            varname = varargin{i}; % make case insensitive
            % check that the supplied name is a valid variable identifier (it does not check if the variable is allowed/declared in defaults, just that it's a possible variable name!)
            if ~isvarname(varname)
              error('NameValuePairToStruct:InvalidName', ...
                 'A variable name was not valid: %s position %i', varname, i);
            % if options are restricted, check that the argument's name exists in the supplied defaults, else we throw an error. With this we can allow only a restricted range of arguments by specifying in the defaults.
            elseif restrict_flag && ~isempty(defaults) && ~any(strmatch(varname, optionNames))
                if restrict_flag ~= 2 % restrict_flag = 2 means that we just ignore this argument, else we show an error
                    error('%s is not a recognized argument name', varname);
                end
            % else alright, we replace the default value for this argument with the user supplied one (or we create the variable if it wasn't in the defaults and there's no restrict_flag)
            else
                argStruct = setfield(argStruct, varname, varargin{i + 1});  %#ok
            end
        end
    
    end
    

    Also available as a Gist.

    And for those interested in having real named arguments (with a syntax similar to Python, eg: myfunction(a=1, b='qwerty'), use InputParser (only for Matlab, Octave users will have to wait until v4.2 at least or you can try a wrapper called InputParser2).

    Also as a bonus, if you don't want to always have to type argstruct.yourvar but directly use yourvar, you can use the following snippet by Jason S:

    function varspull(s)
    % Import variables in a structures into the local namespace/workspace
    % eg: s = struct('foo', 1, 'bar', 'qwerty'); varspull(s); disp(foo); disp(bar);
    % Will print: 1 and qwerty
    % 
    %
    % Author: Jason S
    %
        for n = fieldnames(s)'
            name = n{1};
            value = s.(name);
            assignin('caller',name,value);
        end
    end
    

提交回复
热议问题