Handling Umnatched Arguments with Function Argument Validation
13 views (last 30 days)
Show older comments
Corban Swain
on 21 Jan 2022
Edited: Marek Reclo
on 7 Oct 2024
My goal is to understand how to capture and passthrough name-value arguments that are not strictly defined in an agruments block, but that I would want to fall through/repass to a inner function call. For those familar with python I'm trying to understand how to get a functionality similar to the syntax def foo(..., **kwargs):; where kwargs holds any "extra" name-value arguments that are not explicitly described in the function definition.
Here's an example of a relavent implemantation case done with the inputParser. With my question being: How do I make this work in a robust way with arguments block syntax?
inputParser implementation
% fancyAverage.m
function output = fancyAverage(varargin)
ip = inputParser();
ip.KeepUnmatched = true;
ip.addRequired('X');
ip.addParameter('Weights', []);
ip.parse(varargin{:});
inputs = ip.Results;
disp('`fancyAverage` parsed inputs:')
disp(inputs)
x = inputs.X;
if ~isreal(inputs.X)
output = fancyAverageComplex(varargin{:});
return
end
if isempty(inputs.Weights)
w = 1;
else
w = inputs.Weights;
end
output = mean(w .* x);
end
function output = fancyAverageComplex(varargin)
ip = inputParser();
ip.addRequired('X');
ip.addParameter('Weights', []);
ip.addParameter('ImWeights', []);
ip.addParameter('DoAverageAbsoluteVal', false);
ip.parse(varargin{:});
inputs = ip.Results;
disp('`fancyAverageComplex` parsed inputs:')
disp(inputs)
x = inputs.X;
if isempty(inputs.Weights)
xWeighted = x;
else
w = inputs.Weights;
if isempty(inputs.ImWeights)
xWeighted = w .* x;
else
iw = inputs.ImWeights;
xWeighted = (w .* real(x)) + 1i * (iw .* imag(x));
end
end
if inputs.DoAverageAbsoluteVal
xWeighted = abs(xWeighted);
end
output = mean(xWeighted);
end
Evaluations
%{
>> fancyAverage([1, 10])
`fancyAverage` parsed inputs:
Weights: []
X: [1 10]
ans =
5.5000
>> fancyAverage([1, 10], 'Weights', [10, 1])
`fancyAverage` parsed inputs:
Weights: [10 1]
X: [1 10]
ans =
10
>> fancyAverage([1 + 1i, 10 + 10i])
`fancyAverage` parsed inputs:
Weights: []
X: [1.0000 + 1.0000i 10.0000 +10.0000i]
`fancyAverageComplex` parsed inputs:
DoAverageAbsoluteVal: 0
ImWeights: []
Weights: []
X: [1.0000 + 1.0000i 10.0000 +10.0000i]
ans =
5.5000 + 5.5000i
>> fancyAverage([1 + 1i, 10 + 10i], 'Weights', [10, 1], 'ImWeights', [1, 1])
`fancyAverage` parsed inputs:
Weights: [10 1]
X: [1.0000 + 1.0000i 10.0000 +10.0000i]
`fancyAverageComplex` parsed inputs:
DoAverageAbsoluteVal: 0
ImWeights: [1 1]
Weights: [10 1]
X: [1.0000 + 1.0000i 10.0000 +10.0000i]
ans =
10.0000 + 5.5000i
%}
How to implement this with the arguments block syntax? Maybe the following:
arguments implementation
% fancyAverage_2.m
function output = fancyAverage_2(x, nvInputs, varargin)
arguments
x
nvInputs.Weights = []
end
arguments (Repeating)
varargin
end
disp('`fancyAverage` name-value inputs:')
disp(nvInputs)
if ~isreal(x)
nvInputsCell = namedargs2cell(nvInputs);
output = fancyAverageComplex_2(x, nvInputsCell{:}, varargin{:});
return
end
% ...
end
function output = fancyAverageComplex_2(x, nvInputs)
arguments
x
nvInputs.Weights = []
nvInputs.ImWeights = []
nvInputs.DoAverageAbsoluteVal = false
end
disp('`fancyAverageComplex` name-value inputs:')
disp(nvInputs)
% ...
end
Evaluations
%{
>> fancyAverage_2([1 + 1i, 10 + 10i], 'Weights', [10, 1], 'ImWeights', [1, 1])
File: fancyAverage_2.m Line: 15 Column: 4
Function argument definition error in fancyAverage_2. Functions with positional
and name-value arguments must define positional arguments first.
%}
This evaluation leads to an error because varargin is interpreted as a positional argument which gets defined after the name-value argument (which is not allowed). I do understand that it would be possible to define the name-value arguments of the called function in the calling function's arguments block; however this seems a bit unmaintainable/hacky to me. I am wondering if there is a better way to do this.
Extending to Positional Arguments
Further it seems that neither the inputParser syntax or the arguments syntax can handle capturing unmatched optional positional arguments. I guess this part of my submission is more of a feature request; however I'd like to be able to do something along the lines of:
% fancyAverage_3.m
function output = fancyAverage_3(x, weights, varPosArgIn, varNamedArgIn)
arguments
x
weights = []
end
arguments (PositionalUnmatched)
varPosArgIn
end
arguments (NamedUnmatched)
varNamedArgIn
end
if ~isreal(x)
varArgInputsCell = [varPosArgIn, namedargs2cell(varNamedArgIn)];
output = fancyAverageComplex_3(x, weights, varArgInputsCell{:});
return
end
% ...
end
function output = fancyAverageComplex_3(x, weights, imWeights, nvInputs)
arguments
x
weights = []
imweights = []
nvInputs.DoAverageAbsoluteVal = false
end
% ...
end
Please let me know your thoughts or any existing work-arounds that I might have missed.
0 Comments
Accepted Answer
Bala Tripura Bodapati
on 7 Feb 2022
Hi Corban Swain
It is my understanding that you are able to handle unmatched name-value arguments using the “inputParser” implementation. But the same thing is not working using arguments syntax block.
Currently the “KeepUnmatchedFeature” of “inputParser” is not supported by function argument validation method. Also, handling unmatched optional positional arguments is not supported by both “inputParser” and “arguments syntax block”.
I have brought this issue to the notice of concerned people and it might be considered for future releases.
The following is the link to documentation of “Function Argument Validation”
2 Comments
Marek Reclo
on 7 Oct 2024
Edited: Marek Reclo
on 7 Oct 2024
@Bala Tripura Bodapati Are there any updates regarding "KeepUnmatchedFeature" for function argument validation method ?
More Answers (0)
See Also
Categories
Find more on Argument Definitions in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!