2
votes

I wrote a test function, and ran into trouble with inputParser

function doit(varargin)
p = inputParser;
p.KeepUnmatched = 1;
% why does this bomb if I put in @ischar ? 
p.addOptional('shape','forgot',@(x) true);
p.addParameter('color', 'k', @(x) true); % 'cause color could be char or triplet
p.parse(varargin{:});
%  more code using the parsed inputs
end

If I run

doit('hello','color','g')

or

doit('color','g')

with the code as above, all works as expected. But if I change the input validation for the optional argument to p.addOptional('shape', 'forgot',@(x) ischar) , I get an error at p.parse(varargin{:})

No value was given for 'g'. Name-value pair arguments require a name followed by a value.

It looks like I'm not understanding what the final argument, the anonymous function that's supposed to validate the input argument. What's the fix?

edit:

Matlab 2015a on Win7 .

edit 2: variational test cases

It appears that exactly what class each argument in varargin is affects the behavior.

function threeout = argthree( varargin)     
p = inputParser; 
p.KeepUnmatched = true; 
%defshape = 'forgot'; 
defshape = 5; 
addOptional(p,'shape',defshape,@isnumeric); 
addParameter(p,'color', 'k',@ischar); % 
parse(p, varargin{:}); 
threeout = p.Results; 
end

There are at least two things that can cause parse() to fail. First: this :

>> argthree('nono','color','vu') 
Error using argthree (line 10) 
No value was given for 'vu'. Name-value pair arguments require a name followed by a value. 

Notice that even though the Optional "shape" is supposed to be tested for numeric, the parser appears to think "nono" is the start of a name-value pair.

Entering either a numer or nothng works: 
>> argthree('color','vu') 
ans = 
color: 'vu' 
shape: 5 
>> argthree(7,'color','vu') 
ans = 
color: 'vu' 
shape: 7

Now replace the addOptional line with no validation: addOptional(p,'shape',defshape );

Get the same results as above for all three calls.

Now change the default to defshape = 'forgot' % a charstring

>> argthree('nono','color','vu') 
Error using argthree (line 10) 
No value was given for 'vu'. Name-value pair arguments require a name followed by a value. 
>> argthree('color','vu') 
ans = 
color: 'vu' 
shape: 'forgot' 
>> argthree(7,'color','vu') 
ans = 
color: 'vu' 
shape: 7 

Now try validating the string input: replace the addOptional line again- addOptional(p,'shape',defshape,@ischar );

>> argthree('nono','color','vu') 
ans = 
color: 'vu' 
shape: 'nono' 
>> argthree('color','vu') 
Error using argthree (line 10) 
No value was given for 'vu'. Name-value pair arguments require a name followed by a value. 
>> argthree(7,'color','vu') 
Error using argthree (line 10) 
The value of 'shape' is invalid. It must satisfy the function: ischar. 

There's something very inconsistent here with how parse() tries to match varargin{:} to the parameter list (optional or name-value pair). Due to the inconsistent failures happening, I can't set up a situation with an Optional char argument followed by a name-value pair, so far as I can see.

update

I've filed a bug report at MathWorks, "Technical Support Case #02121531" and they were able to reproduce the problem .

3
what if you repeat x in the anonymous fcn? p.addOptional('shape', 'forgot', @(x) ischar(x) ) - Trogdor
which version of matlab are you using? - Trogdor
It also looks like inputParser has trouble with mixing addOptional and addParameter. In this example, addOptional seems to become a mandatory 1st input argument (in 2015b) - Trogdor

3 Answers

1
votes

tl;dr try @ischar or @(x) ischar(x)instead of @(x) ischar

Explanation:

I suspect that the error message is not appropriate, and is merely a generic error that results from your validation function throwing an error. Matlab allows two kinds of validation function:

  • One that returns true / false, in which case an appropriate error is generated
  • or one that simply f***s up and throws an exception, at which point parsing fails.

I suspect that in the latter case, matlab just spurts out a generic uninformative error message about you having failed to pass appropriate parameters.

I think your problem is probably that, inside your anonymous function, you are effectively calling ischar (which is a function name) without arguments, which results in an error. This throws an exception, and matlab interprets this as if your validation function threw an exception to signal that parsing has failed.

Therefore replace @(x) ischar with @ischar or @(x) ischar(x) (which are both valid function handles corresponding to single-argument functions), and it should work (assuming there's no other bugs).

1
votes

I have been discussing this with MathWorks, Technical Support Case #02121531 .

I wrote an explanation of exactly what I was hoping to do:

I would like to be able to have one Optional argument which is a character string, and then one or more Value-Parameter pairs, for which the parameters are strings. Since I interpreted "Optional" as being just that, i.e. one of the varargin{:} elements which does not need to be present, I expect that both of the following commands would work:

foop =parseCheck('nono','color','vu');

and

foop =parseCheck('color','vu');

However, the parser function appears unable to understand that " 'color' " is a Value in a Value-Parameter pair. The parser sees a character string as the first varargin and immediately identifies it as the Optional parameter. My view is that this is neither intuitive nor desirable.

I think this is a bug, and would recommend that the parser code be rewritten so that any string which is a match to any "Value" string be first tested to see if it leads to a valid Value-Parameter pair, and only after that should the initial varargin{:} elements be tested against Optional argument validation.

Basically, I'm saying that you can't call an argument "Optional" if it has to be present for the parser to function properly.

The last I have found out is, quoting their support person,

Thank you for your patience while I was investigating the issue. In the example that you sent, ‘color’ is indeed consumed as the optional argument ‘Shape’. This is happening because currently, positional arguments take precedence over name/value pairs. I do apologize for the inconvenience this may cause you. I have shared your workflow, concerns and your suggestions with the developers and an enhancement request has been submitted. It is to be considered in future releases of MATLAB.

0
votes

This isn't actually a bug. It's an issue with your validation functions not being specific enough. Using just ischar as a validation function means a parameter name can be misinterpreted as a valid value. In all likelihood, I would expect your inputs to have a fixed set of valid values, and you should instead be checking that the input is a valid member of that set. Here's an example:

p = inputParser;
addOptional(p, 'shape', 'forgot', @(s) ismember(s, {'square', 'circle', 'triangle'}));
addParameter(p, 'color', 'k', @(c) ismember(c, 'ymcrgbwk'));

You can make your validation functions more complicated as necessary, probably foregoing anonymous functions for function handles to local functions instead. For instance, you can check the data type then check the values, or you can check that the 'color' argument is either a single character or an RGB triplet.

Part of what may have been causing confusion for you was setting 'KeepUnmatched' to true, which will store unmatched parameter-value pairs and not throw any errors about them not matching expected parameters of your inputParser object. The first example of your second edit (with numeric used as the validation function) does not find a numeric value in the input argument list, thus setting 'shape' to its default. Since the parser next looks for a parameter-value pair, the strings 'nono' and 'color' become an unmatched pair and are stored in the 'Unmatched' property of the parser object. Still looking for a valid parameter-value pair, the parser only finds 'vu', assumed to be an unmatched parameter string.

The general rule of thumb I follow: Make your validation functions as specific as possible.