How to handle pairs of name / value pairs of function arguments in MATLAB

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

function example(varargin) % Lots of set up stuff vargs = varargin; nargs = length(vargs); names = vargs(1:2:nargs); values = vargs(2:2:nargs); validnames = {'foo', 'bar', 'baz'}; for name = names validatestring(name{:}, validnames); end % Do something ... foo = strmatch('foo', names); disp(values(foo)) end example('foo', 1:10, 'bar', 'qwerty') 

It seems like a lot of effort is put into extracting the corresponding values ​​(and it is still not particularly reliable again with incorrectly specified inputs). Is there a better way to handle these name / value pairs? Are there any helper functions that come with MATLAB to help?

+51
function arguments matlab function-parameter
May 05 '10 at 17:03
source share
13 answers

I prefer to use structures for my options. This gives you an easy way to store parameters and an easy way to define them. In addition, all this becomes quite 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 
+49
May 05 '10 at 19:40
source share

InputParser helps with this. For more information, see Analysis Parameter Inputs .

+38
May 05 '10 at 17:47
source share

I could scroll this for hours, but I don't have a good gestalt representation of the overall Matlab signature processing. But here are some tips.

First take the laissez faire approach to test input types. Trust the caller. If you really need strong type testing, you need a static language like Java. Try to ensure type safety wherever you are in Matlab, and you will end up with a significant portion of your LOC and runtime designed to test runtime and enforcement in userland, which trades at high speed and Matlab development speed. I learned this hard way.

For API signatures (functions intended to be called from other functions, not from the command line), consider using a single Args argument instead of varargin. It can then be passed between multiple arguments without having to convert it to and from a comma-separated list for varargin signatures. Structures, Jonas says, are very comfortable. There is also a good isomorphism between n-by-2 structures and cells {name, value; ...}, and you can set up a couple of functions to convert between them inside your functions depending on what it wants to use inside.

 function example(args) %EXAMPLE % % Where args is a struct or {name,val;...} cell array 

Regardless of whether you use an input parser or collapse your own name / shaft parser, like these other great examples, pack it into a separate standard function that you will call from the top of your functions that have a name / sign signature. Whether to accept a list of default values ​​in the data structure that is convenient to write, and your calls to analyze arguments will look like function signature declarations, which simplifies reading and avoids copy and paste template templates.

Parsing calls will look here.

 function out = my_example_function(varargin) %MY_EXAMPLE_FUNCTION Example function % No type handling args = parsemyargs(varargin, { 'Stations' {'ORD','SFO','LGA'} 'Reading' 'Min Temp' 'FromDate' '1/1/2000' 'ToDate' today 'Units' 'deg. C' }); fprintf('\nArgs:\n'); disp(args); % With type handling typed_args = parsemyargs(varargin, { 'Stations' {'ORD','SFO','LGA'} 'cellstr' 'Reading' 'Min Temp' [] 'FromDate' '1/1/2000' 'datenum' 'ToDate' today 'datenum' 'Units' 'deg. C' [] }); fprintf('\nWith type handling:\n'); disp(typed_args); % And now in your function body, you just reference stuff like % args.Stations % args.FromDate 

And here is a function to implement name / val parsing this way. You could release it and replace it with an input parser, your own type conventions, etc. I think the n-by-2 cell convention does for well-readable source code; Think about it. Structures are generally more convenient for processing in the receive code, but n-by-2 cells are more convenient for building using expressions and literals. (Structures require the continuation of ", ..." in each row and protection of cell values ​​from expansion to non-scalar structures.)

 function out = parsemyargs(args, defaults) %PARSEMYARGS Arg parser helper % % out = parsemyargs(Args, Defaults) % % Parses name/value argument pairs. % % Args is what you pass your varargin in to. It may be % % ArgTypes is a list of argument names, default values, and optionally % argument types for the inputs. It is an n-by-1, n-by-2 or n-by-3 cell in one % of these forms forms: % { Name; ... } % { Name, DefaultValue; ... } % { Name, DefaultValue, Type; ... } % You may also pass a struct, which is converted to the first form, or a % cell row vector containing name/value pairs as % { Name,DefaultValue, Name,DefaultValue,... } % Row vectors are only supported because it unambiguous when the 2-d form % has at most 3 columns. If there were more columns possible, I think you'd % have to require the 2-d form because 4-element long vectors would be % ambiguous as to whether they were on record, or two records with two % columns omitted. % % Returns struct. % % This is slow - don't use name/value signatures functions that will called % in tight loops. args = structify(args); defaults = parse_defaults(defaults); % You could normalize case if you want to. I recommend you don't; it a runtime cost % and just one more potential source of inconsistency. %[args,defaults] = normalize_case_somehow(args, defaults); out = merge_args(args, defaults); %% function out = parse_defaults(x) %PARSE_DEFAULTS Parse the default arg spec structure % % Returns n-by-3 cellrec in form {Name,DefaultValue,Type;...}. if isstruct(x) if ~isscalar(x) error('struct defaults must be scalar'); end x = [fieldnames(s) struct2cell(s)]; end if ~iscell(x) error('invalid defaults'); end % Allow {name,val, name,val,...} row vectors % Does not work for the general case of >3 columns in the 2-d form! if size(x,1) == 1 && size(x,2) > 3 x = reshape(x, [numel(x)/2 2]); end % Fill in omitted columns if size(x,2) < 2 x(:,2) = {[]}; % Make everything default to value [] end if size(x,2) < 3 x(:,3) = {[]}; % No default type conversion end out = x; %% function out = structify(x) %STRUCTIFY Convert a struct or name/value list or record list to struct if isempty(x) out = struct; elseif iscell(x) % Cells can be {name,val;...} or {name,val,...} if (size(x,1) == 1) && size(x,2) > 2 % Reshape {name,val, name,val, ... } list to {name,val; ... } x = reshape(x, [2 numel(x)/2]); end if size(x,2) ~= 2 error('Invalid args: cells must be n-by-2 {name,val;...} or vector {name,val,...} list'); end % Convert {name,val, name,val, ...} list to struct if ~iscellstr(x(:,1)) error('Invalid names in name/val argument list'); end % Little trick for building structs from name/vals % This protects cellstr arguments from expanding into nonscalar structs x(:,2) = num2cell(x(:,2)); x = x'; x = x(:); out = struct(x{:}); elseif isstruct(x) if ~isscalar(x) error('struct args must be scalar'); end out = x; end %% function out = merge_args(args, defaults) out = structify(defaults(:,[1 2])); % Apply user arguments % You could normalize case if you wanted, but I avoid it because it a % runtime cost and one more chance for inconsistency. names = fieldnames(args); for i = 1:numel(names) out.(names{i}) = args.(names{i}); end % Check and convert types for i = 1:size(defaults,1) [name,defaultVal,type] = defaults{i,:}; if ~isempty(type) out.(name) = needa(type, out.(name), type); end end %% function out = needa(type, value, name) %NEEDA Check that a value is of a given type, and convert if needed % % out = needa(type, value) % HACK to support common 'pseudotypes' that aren't real Matlab types switch type case 'cellstr' isThatType = iscellstr(value); case 'datenum' isThatType = isnumeric(value); otherwise isThatType = isa(value, type); end if isThatType out = value; else % Here you can auto-convert if you're feeling brave. Assumes that the % conversion constructor form of all type names works. % Unfortunately this ends up with bad results if you try converting % between string and number (you get Unicode encoding/decoding). Use % at your discretion. % If you don't want to try autoconverting, just throw an error instead, % with: % error('Argument %s must be a %s; got a %s', name, type, class(value)); try out = feval(type, value); catch err error('Failed converting argument %s from %s to %s: %s',... name, class(value), type, err.message); end end 

So unfortunate that strings and datamines are not first-class types in Matlab.

+11
May 05 '10 at 21:44
source share

Read Lauren 's informational post on this subject. Do not forget to read the comments section ... - You will see that there are quite a few different approaches to this topic. They all work, so the choice of the preferred method really depends on personal taste and maintainability.

+5
May 05 '10 at 20:09
source share

Personally, I use a custom function derived from a private method used by many functions of the Statistical Tool (e.g. kmeans, pca, svmtrain, ttest2, ...)

Being an internal utility function, it has changed and has been renamed many times by version. Depending on your version of MATLAB, try finding one of the following files:

 %# old versions which -all statgetargs which -all internal.stats.getargs which -all internal.stats.parseArgs %# current one, as of R2014a which -all statslib.internal.parseArgs 

As with any undocumented function, there are no guarantees, and it can be removed from MATLAB in subsequent releases without any notice ... In any case, I believe that someone published its old version as getargs in file sharing .

The function processes the parameters as name / value pairs, using a set of valid parameter names along with their default values. It returns the parsed parameters as separate output variables. By default, unrecognized name / value pairs cause an error, but we can also write them to additional output without interference. Here is the function description:

$MATLABROOT\toolbox\stats\stats\+internal\+stats\parseArgs.m

 function varargout = parseArgs(pnames, dflts, varargin) % % [A,B,...] = parseArgs(PNAMES, DFLTS, 'NAME1',VAL1, 'NAME2',VAL2, ...) % PNAMES : cell array of N valid parameter names. % DFLTS : cell array of N default values for these parameters. % varargin : Remaining arguments as name/value pairs to be parsed. % [A,B,...]: N outputs assigned in the same order as the names in PNAMES. % % [A,B,...,SETFLAG] = parseArgs(...) % SETFLAG : structure of N fields for each parameter, indicates whether % the value was parsed from input, or taken from the defaults. % % [A,B,...,SETFLAG,EXTRA] = parseArgs(...) % EXTRA : cell array containing name/value parameters pairs not % specified in PNAMES. 



Example:

 function my_plot(x, varargin) %# valid parameters, and their default values pnames = {'Color', 'LineWidth', 'LineStyle', 'Title'}; dflts = { 'r', 2, '--', []}; %# parse function arguments [clr,lw,ls,txt] = internal.stats.parseArgs(pnames, dflts, varargin{:}); %# use the processed values: clr, lw, ls, txt %# corresponding to the specified parameters %# ... end 

Now this sample function can be called any of the following methods:

 >> my_plot(data) %# use the defaults >> my_plot(data, 'linestyle','-', 'Color','b') %# any order, case insensitive >> my_plot(data, 'Col',[0.5 0.5 0.5]) %# partial name match 

The following are some invalid calls and errors:

 %# unrecognized parameter >> my_plot(x, 'width',0) Error using [...] Invalid parameter name: width. %# bad parameter >> my_plot(x, 1,2) Error using [...] Parameter name must be text. %# wrong number of arguments >> my_plot(x, 'invalid') Error using [...] Wrong number of arguments. %# ambiguous partial match >> my_plot(x, 'line','-') Error using [...] Ambiguous parameter name: line. 



inputParser:

As already mentioned, the officially recommended approach to parsing function inputs is to use inputParser . It supports various schemes, such as specifying the required inputs, optional positional arguments, and name / value parameters. It also allows validation on inputs (e.g. checking the class / type and size / shape of arguments)

+5
May 6 '10 at 12:53
source share

I am more a fan of homemade boiler plate code:

 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 

In this way, I can simulate a pair of "option" parameters, which is almost identical to how most Matlab functions accept their arguments.

Hope this helps,

Will

+3
May 05 '10 at 19:19
source share

Here, the solution I am testing is based on the idea of ​​Jonas.

 function argStruct = NameValuePairToStruct(defaults, varargin) %NAMEVALUEPAIRTOSTRUCT Converts name/value pairs to a struct. % % ARGSTRUCT = NAMEVALUEPAIRTOSTRUCT(DEFAULTS, VARARGIN) converts % name/value pairs to a struct, with defaults. The function expects an % even number of arguments to VARARGIN, alternating NAME then VALUE. % (Each NAME should be a valid variable name.) % % Examples: % % No defaults % NameValuePairToStruct(struct, ... % 'foo', 123, ... % 'bar', 'qwerty', ... % 'baz', magic(3)) % % With defaults % NameValuePairToStruct( ... % struct('bar', 'dvorak', 'quux', eye(3)), ... % 'foo', 123, ... % 'bar', 'qwerty', ... % 'baz', magic(3)) % % See also: inputParser nArgs = length(varargin); if rem(nArgs, 2) ~= 0 error('NameValuePairToStruct:NotNameValuePairs', ... 'Inputs were not name/value pairs'); end argStruct = defaults; for i = 1:2:nArgs name = varargin{i}; if ~isvarname(name) error('NameValuePairToStruct:InvalidName', ... 'A variable name was not valid'); end argStruct = setfield(argStruct, name, varargin{i + 1}); %#ok<SFLD> end end 
+1
May 6 '10 at 9:40 a.m.
source share

Inspired by Jonas answer, but more compact:

 function example(varargin) defaults = struct('A',1, 'B',magic(3)); %define default values params = struct(varargin{:}); for f = fieldnames(defaults)', if ~isfield(params, f{1}), params.(f{1}) = defaults.(f{1}); end end %now just access them as params.A, params.B 
+1
Jan 13 '14 at 18:48
source share
 function argtest(varargin) a = 1; for ii=1:length(varargin)/2 [~] = evalc([varargin{2*ii-1} '=''' num2str(varargin{2*ii}) '''']); end; disp(a); who 

This, of course, does not check the correct assignments, but it is simple and any useless variable will be ignored anyway. It also works only for numbers, strings, and arrays, but not for matrices, cells, or structures.

0
Aug 11 '10 at 13:17
source share

I ended up writing it today, and then I found these mentions. Mine uses struct and struct 'overlays' for options. It substantially reflects the functionality of setstructfields (), except that new parameters cannot be added. It also has the ability to recurse, while setstructfields () does this automatically. It can take paired values ​​in an array of cells 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 would say that using the naming rules 'defaults' and 'new' would probably be better: P

0
Sep 11 2018-11-11T00:
source share

I made a function based on Jonas and Richie Cotton. It implements both functionalities (flexible arguments or restrictions, which means that only variables existing in the default values ​​are allowed), as well as several other things, such as sugar parsing and health 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 inside a sub-struct (happens on Octave), because anyway it 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 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 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 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 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 no restrict_flag) else argStruct = setfield(argStruct, varname, varargin{i + 1}); %#ok<SFLD> end end end 

Also available as Gist .

And for those who are interested in real named arguments (with syntax similar to Python, for example: 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 always want to print argstruct.yourvar , but use yourvar directly, you can use the following snippet from 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 
0
Jun 10 '14 at 10:05
source share

With age, I use process_options.m . It is stable, easy to use and included in various Matlab systems. I don’t know anything about performance, although it’s possible that there are faster implementations.

The function that process_options enjoys the most is the unused_args return value, which can be used to separate input arguments in argument groups for, for example, subprocesses.

And you can easily determine the default values.

Another good thing: using process_options.m usually results in readable and supported options.

0
Sep 23 '14 at 8:08
source share

There is an excellent feature called parsepvpairs that will take care of this if you have access to MATLAB finance tools. It takes three arguments, the expected field names, default field values, and the actual arguments received.

For example, here is a function that creates an HTML shape in MATLAB and can take optional pairs of field values ​​with the names "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
Dec 07 '16 at 18:10
source share



All Articles