Handling Umnatched Arguments with Function Argument Validation

10 vues (au cours des 30 derniers jours)
Corban Swain
Corban Swain le 21 Jan 2022
Commenté : Corban Swain le 13 Fév 2022
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.

Réponse acceptée

Bala Tripura Bodapati
Bala Tripura Bodapati le 7 Fév 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”
  1 commentaire
Corban Swain
Corban Swain le 13 Fév 2022
Thanks so much, good to know it might be considered.

Connectez-vous pour commenter.

Plus de réponses (0)

Catégories

En savoir plus sur Argument Definitions dans Help Center et File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by