I have a MATLAB file that contains a single top-level function, called sandbox
. That function in turn contains two nested functions, mysum
and myprod
, which are identical in functionality and what parameters they allow except that one uses @sum
internally and the other uses @prod
internally. My goal is to create a wrapper function to use in both mysum
and myprod
that takes care of all the validation and input parsing. This function is called applyFunc
.
Here's where it gets tricky. mysum
and myprod
come in two forms:
mysum(v)
returns sum(v, 1)
. mysum(v, 'imag')
returns sum(v, 1) + 1i
Any other combinations of input should throw an error.
I'm having trouble using inputParser
to parse these various combinations of input, specifically the optional string input. Here's the code:
function sandbox()
%% Data
v = [1 4; 3 3];
%% Calculations
s = mysum(v);
si = mysum(v, 'imag');
p = myprod(v);
pi = myprod(v, 'imag');
%% Accuracy tests
assert(isequal(s, [4 7]))
assert(isequal(si, [4+1i 7+1i]))
assert(isequal(p, [3 12]))
assert(isequal(pi, [3+1i 12+1i]))
function x = mysum(varargin)
x = applyFunc(@sum, varargin{:});
end
function x = myprod(varargin)
x = applyFunc(@prod, varargin{:});
end
end
function x = applyFunc(func, varargin)
p = inputParser();
p.addRequired('func', @(x) validateattributes(x, {'function_handle'}, {'scalar'}));
p.addRequired('v', @(x) validateattributes(x, {'double'}, {}, 'applyFunc:msg', 'v'));
p.addOptional('imag', '', @(x) validatestring(x, {'imag', ''})); % THIS LINE IS THE PROBLEM
p.parse(func, varargin{:});
f = p.Results.func;
v = p.Results.v;
strflag = p.Results.imag;
x = f(v);
if ~isempty(strflag)
validatestring(strflag, {'imag'});
x = x + 1i;
end
end
The line that's causing the problem is this one (as marked in the code above):
p.addOptional('imag', '', @(x) validatestring(x, {'imag', ''}));
The documentation for inputParser says that:
For optional string inputs, specify a validation function. Without a validation function, the input parser interprets valid string inputs as invalid parameter names and throws an error.
Unfortunately I don't have any idea how to do this. Is there something simple Im missing or what? If the 'imag'
argument isn't passed at all (as in the assignment of s
and p
), the code works fine, but if I do pass it, I get this error:
Error using sandbox>applyFunc (line 32)
The value of 'imag' is invalid. It must satisfy the function:
@(x)validatestring(x,{'imag',''}).
Error in sandbox/mysum (line 18)
x = applyFunc(@sum, varargin{:});
Error in sandbox (line 7)
si = mysum(v, 'imag');
Any help?
The problem is that validatestring
returns the matching string from the cell argument ({'imag',''}
) rather than a Boolean indicating if it passes validation. Instead, use strcmp
and any
:
@(x) any(strcmp(x,{'imag', ''}))
Also, with validatestring
, if the input string did not match either 'imag'
or ''
(actually just 'imag'
since empty strings only match in R2014a+), it would throw an error rather than returning false so that the inputParser
could return the appropriate error.
Another nice way to fix the problem is to change the syntax of applyFunc
entirely so that instead of just 'imag'
as an optional string input argument, use a Parameter-Value with 'imag'
as the parameter and a validated boolean as the input.
The input definition suggested by Amro in the comments:
p.addParameter('imag', false, @(x)validateattributes(x, {'logical'}, {'scalar'}))
The usage:
mysum(x,'imag',true)
mysum(x) % default is equivalent to mysum(x,'imag',false)
This would simplify the rest of the code with p.Result.imag
being a logical
scalar. I would suggest:
x = f(v) + p.Result.imag*1i;