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

后端 未结 14 1022
忘掉有多难
忘掉有多难 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条回答
  • 2020-11-28 19:37

    Read Loren's informative post on this issue. Don't forget to read the comments section... - You will see that there are quite a few different approaches to this topic. They all work, so selecting a prefered method is really a matter of personal taste and maintainability.

    0 讨论(0)
  • 2020-11-28 19:37

    I'm a bigger fan of home-grown boiler plate code like this:

    function TestExample(req1, req2, varargin)
    for i = 1:2:length(varargin)
        if strcmpi(varargin{i}, 'alphabet')
            ALPHA = varargin{i+1};
    
        elseif strcmpi(varargin{i}, 'cutoff')
            CUTOFF = varargin{i+1};
            %we need to remove these so seqlogo doesn't get confused
            rm_inds = [rm_inds i, i+1]; %#ok<*AGROW>
    
        elseif strcmpi(varargin{i}, 'colors')
            colors = varargin{i+1};
            rm_inds = [rm_inds i, i+1]; 
        elseif strcmpi(varargin{i}, 'axes_handle')
            handle = varargin{i+1};
            rm_inds = [rm_inds i, i+1]; 
        elseif strcmpi(varargin{i}, 'top-n')
            TOPN = varargin{i+1};
            rm_inds = [rm_inds i, i+1];
        elseif strcmpi(varargin{i}, 'inds')
            npos = varargin{i+1};
            rm_inds = [rm_inds i, i+1];
        elseif strcmpi(varargin{i}, 'letterfile')
            LETTERFILE = varargin{i+1};
            rm_inds = [rm_inds i, i+1];
        elseif strcmpi(varargin{i}, 'letterstruct')
            lo = varargin{i+1};
            rm_inds = [rm_inds i, i+1];
        end
    end
    

    This way I can simulate the 'option', value pair that's nearly identical to how most Matlab functions take their arguments.

    Hope that helps,

    Will

    0 讨论(0)
  • 2020-11-28 19:42

    I prefer using structures for my options. This gives you an easy way to store the options and an easy way to define them. Also, the whole thing becomes rather compact.

    function example(varargin)
    
    %# define defaults at the beginning of the code so that you do not need to
    %# scroll way down in case you want to change something or if the help is
    %# incomplete
    options = struct('firstparameter',1,'secondparameter',magic(3));
    
    %# read the acceptable names
    optionNames = fieldnames(options);
    
    %# count arguments
    nArgs = length(varargin);
    if round(nArgs/2)~=nArgs/2
       error('EXAMPLE needs propertyName/propertyValue pairs')
    end
    
    for pair = reshape(varargin,2,[]) %# pair is {propName;propValue}
       inpName = lower(pair{1}); %# make case insensitive
    
       if any(strcmp(inpName,optionNames))
          %# overwrite options. If you want you can test for the right class here
          %# Also, if you find out that there is an option you keep getting wrong,
          %# you can use "if strcmp(inpName,'problemOption'),testMore,end"-statements
          options.(inpName) = pair{2};
       else
          error('%s is not a recognized parameter name',inpName)
       end
    end
    
    0 讨论(0)
  • 2020-11-28 19:45

    InputParser helps with this. See Parse Function Inputs for more information.

    0 讨论(0)
  • 2020-11-28 19:45

    There is a nifty function called parsepvpairs that takes care of this nicely, provided you have access to MATLAB's finance toolbox. It takes three arguments, expected field names, default field values, and the actual arguments received.

    For example, here's a function that creates an HTML figure in MATLAB and can take the optional field value pairs named 'url', 'html', and 'title'.

    function htmldlg(varargin)
        names = {'url','html','title'};
        defaults = {[],[],'Padaco Help'};
        [url, html,titleStr] = parsepvpairs(names,defaults,varargin{:});
    
        %... code to create figure using the parsed input values
    end
    
    0 讨论(0)
  • 2020-11-28 19:45

    I ended up writing this today, and then found these mentions. Mine uses struct's and struct 'overlays' for options. It essentially mirrors the functionality of setstructfields() except that new parameters can not be added. It also has an option for recursing, whereas setstructfields() does it automatically. It can take in a cell array of paired values by calling struct(args{:}).

    % Overlay default fields with input fields
    % Good for option management
    % Arguments
    %   $opts - Default options
    %   $optsIn - Input options
    %       Can be struct(), cell of {name, value, ...}, or empty []
    %   $recurseStructs - Applies optOverlay to any existing structs, given new
    %   value is a struct too and both are 1x1 structs
    % Output
    %   $opts - Outputs with optsIn values overlayed
    function [opts] = optOverlay(opts, optsIn, recurseStructs)
        if nargin < 3
            recurseStructs = false;
        end
        isValid = @(o) isstruct(o) && length(o) == 1;
        assert(isValid(opts), 'Existing options cannot be cell array');
        assert(isValid(optsIn), 'Input options cannot be cell array');
        if ~isempty(optsIn)
            if iscell(optsIn)
                optsIn = struct(optsIn{:});
            end
            assert(isstruct(optsIn));
            fields = fieldnames(optsIn);
            for i = 1:length(fields)
                field = fields{i};
                assert(isfield(opts, field), 'Field does not exist: %s', field);
                newValue = optsIn.(field);
                % Apply recursion
                if recurseStructs
                    curValue = opts.(field);
                    % Both values must be proper option structs
                    if isValid(curValue) && isValid(newValue) 
                        newValue = optOverlay(curValue, newValue, true);
                    end
                end
                opts.(field) = newValue;
            end
        end
    end
    

    I'd say that using the naming convention 'defaults' and 'new' would probably be better :P

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