MATLAB Answers

1

Allowing unknown parameters in an inputParser

Asked by D. Plotnick on 16 Apr 2018
Latest activity Edited by Matt J
on 17 Apr 2018
Hello, I am attempting to figure out a better way of handling parameters and passing them through input parsers. Let us say I have three parameters, and I will store them in a struct, and then pass that struct as the arguments to multiple input parsers that DON'T explicitly include the stated variables without it throwing errors.
params = struct(...
'type','fruit',...
'name','banana',...
'price',3.14)
Ok, now lets say I want to hand this as the parameters to two different functions. This first one sets a default price for some reason,
function tot = myFun1(n,varargin)
defaultPrices = struct(...
'apple',ln(1),...
'banana',sqrt(-1),...
'orange',3.14);
p = inputParser
addParameter(p,'name','orange',@(x) ismember(x,{'apple','orange','banana'}));
addParameter(p,'price',[])
p = parse(p,varargin{:})
price = p.Results.price;
if isempty(price)
price = defaultPrices.(p.Results.name);
end
tot = n*price;
end
and this second one lets say pulls up a picture.
function myFun2(varargin)
defaultImage = 'ErrorScreen.png'
p = inputParser;
addParameter(p,'type',[]);
addParameter(p,'name',[]);
parse(p,varargin{:})
try
im = imload(fullfile(pwd,p.Results.type,p.Results.name));
catch
im = imload(defaultImage);
end
imshow(im);
end
Ok, now for the two functions above I want to be able to call both with the same params structure:
tot = myFun1(1E6,params);
myFun2(params);
However, this will lead to an error in both functions; the first because 'type' is not explicitly included in the input parser, "'type' is not a recognized parameter. For a list of valid name-value pair arguments, see the documentation for this function.", and similarly for myFun2 due to 'price.
So, my question is whether (A) there is a way to get the parser to be more tolerant of unexpected input fields, so I can do the above, or (B) if there is some other "best practices" method so that I can avoid creating multiple params structures with redundant fields, or duplicating the structure, and then cycling through 'rmfield's before input. Neither is terribly elegant, and I am trying to avoid lots of 'addParameter's for params that I don't actually need in a specific function just to avoid the error.
The above is just a goofy MWE of what I am trying to code, help is appreciated.

  2 Comments

As a follow-up: being able to add an empty argument as the input validator would also be helpful. In that case, we could instead use an nX3 cell array and a loop to construct our parser: e.g.
defaults = {...
'type','fruit',@(x) ismember(x,allowedTypes) ; ...
'name','orange',[]; ...
'price',3.14, []};
for i = 1:size(defaults,1)
addParameter(p,defaults{i,1},defaults{i,2},defaults{i,3});
end
Using the cell technique would allow me to carry around a set of 'default function parameters'. However, the above results in the error "Validator must be a function handle." I suppose I could just create a dummy function...but again that is pretty kludgy.
Follow up to my follow up: apparently using '@true' will act as a valid dummy function in the cell array.

Sign in to comment.

2 Answers

Answer by Matt J
on 16 Apr 2018
Edited by Matt J
on 16 Apr 2018
 Accepted Answer

You can set the inputParser object's KeepUnmatched property to true

p = inputParser;
p.KeepUnmatched=true;
addParameter(p,...)

This way, no error will be thrown if an unexpected parameter name is passed. It is a dangerous practice, however. If you make a spelling mistake in your params input, you will be passing incorrect parameter values unwittingly.

  2 Comments

Thanks, and yes I can see that being a danger, since it will automatically use the default value without further notification.
I have opted to use my cell array strategy outlined above. Extraneous values wind up in the p.Results field in each function, but they appear to be handles to the original variables rather than copies, so there isn't any memory bloat (please correct me if I'm wrong). I then pull the needed values out of p.Results for each function. This has the advantage of allowing me to declare all of my default parameters in a single script, rather than have them set at the top of each function. There will be issues if I wanted, say, two different function dependent defaults for the same named parameter, but overall its a better strategy and less prone to developer error.

Sign in to comment.


Answer by Matt J
on 17 Apr 2018
Edited by Matt J
on 17 Apr 2018

(B) if there is some other "best practices" method so that I can avoid creating multiple params structures with redundant fields

An alternative would be to use a tree of nested structures,

 allParams.myFun1.name =...
 allParams.myFun1.price =...
 allParams.myFun2.name =...
 allParams.myFun2.type =...

and make function calls like

   tot = myFun1(1E6,allParams.myFun1);
   myFun2(allParams.myFun2)

This involves no more storage than the total number of default parameters across the different functions. It also avoids name conflict between functions with the same parameter names.

  0 Comments

Sign in to comment.