Parse function parameters using InputParser class with unknown number of parameters and optional name-value pairs parameters

37 visualizzazioni (ultimi 30 giorni)
Hi Community,
What would it be the right way to parse a function with inputParser class that accepts an aribitrary number of parameters assigned through the usual varargin with some optional name-value pairs parameters?
This example showing two different function calls of vprintf should illustrate the problem:
verbosity = 2;
vprintf(verbosity, "onNewLine", true, "this is a string: '%s', and this is a number: '%g'\n", "tuna" , 100.1);
vprintf(verbosity, "this is a string: '%s', and this is a number: '%g'\n", "tuna" , 100.1);
where:
  • verbosity is mandatory,
  • onNewLine name with its following value true are optional ,
  • and everything after is essentially optional.
This crude attempt did't work because the parser seems to accept name-value pairs arguments only:
function vprintf(verbosity, varargin)
p = inputParser;
addOptional(p,'onNewLine', 0);
parse(p, varargin{:});
disp(p.Results);
end
And it gives the following error
vprintf(1,"onNewLine", true, "aaaaaaaaaaa");
Error using vprintf (line 4)
'aaaaaaaaaaa' is not a recognized parameter.

Risposta accettata

Shlok
Shlok il 1 Nov 2024 alle 4:20
Hi Bob,
The “inputParser” function does supports passing arbitrary number of parameters and then name-value pairs, but those parameters need to be explicitly specified within the parsing function. Since the non-name-value pair parameters are not exhaustive, it becomes difficult to differentiate them with name-value pairs and hence parsing them requires a different approach.
Hence, to parse appropriately, you can specify the number of name-value pairs as a required argument (say numNameValuePairs). And then pass name-value pairs, followed by the optional parameters. Within the parsing function, name-value pairs can be separated out from varargin array using the previously supplied total count, and thus can be parsed using inputParser. Remaining arguments can be extracted using indexing from varargin” array.
I have written a sample code that does the same:
% verbosity - taken from your example
% numNameValuePairs – numeric input, gives the count of name-value pairs
function vprintf(verbosity, numNameValuePairs, varargin)
p = inputParser;
% Loop through varargin to separate name-value pairs from remaining arguments
i = 1;
while i <= numNameValuePairs * 2
if (ischar(varargin{i}) || isstring(varargin{i})) && i < length(varargin) && (islogical(varargin{i+1}) || isnumeric(varargin{i+1}) || ischar(varargin{i+1}) || isstring(varargin{i+1}))
addParameter(p, varargin{i}, varargin{i+1})
i = i + 2;
else
error('Expected name-value pairs are not correctly formatted.');
end
end
% Parse the name-value pairs
parse(p, varargin{1:numNameValuePairs * 2});
% Extract the remaining arguments
remainingArgs = varargin(numNameValuePairs * 2 + 1:end);
fprintf('Verbosity: %d\n', verbosity);
disp('Parsed name-value pairs:');
disp(p.Results);
fprintf('Remaining arguments: ');
disp(remainingArgs);
end
verbosity = 2;
numNameValuePairs = 1;
vprintf(verbosity, numNameValuePairs, "onNewLine", true, "this is a string: '%s', and this is a number: '%g'\n", "tuna" , 100.1);
Verbosity: 2 Parsed name-value pairs: onNewLine: 1 Remaining arguments: {["this is a string: '%s', and this is a number: '..."]} {["tuna"]} {[100.1000]}
vprintf(verbosity, numNameValuePairs, "onNewLine", true);
Verbosity: 2 Parsed name-value pairs: onNewLine: 1 Remaining arguments:
vprintf(1, numNameValuePairs, "onNewLine" , true, "%d, %f, %s %d %d\n", 10, 123.4, "string", 1,2);
Verbosity: 1 Parsed name-value pairs: onNewLine: 1 Remaining arguments: {["%d, %f, %s %d %d\n"]} {[10]} {[123.4000]} {["string"]} {[1]} {[2]}
Thus, the above code parses both name-value pairs and additional parameters supplied in the function.
You can refer to the following MathWorks Documentation link to know more about “inputParser” function:

Più risposte (2)

Raghava S N
Raghava S N il 28 Ott 2024 alle 5:12
Hi,
From my understanding, you want to use a variable number of optional parameters without having explicitly declared them in the input parser. And you want these arbitrary number of parameters assigned through the varargin” functionality.
TheKeepUnmatched” property of the inputParser is a matching indicator that throws an error when an input is not found in the input parser scheme. By default, the parse function throws an error if an input argument name does not match one defined in the input parser scheme.
To suppress the error and store the input argument name and value, set KeepUnmatched to true (or 1). The inputParser object stores unmatched input argument names and values in the Unmatched property. Refer to this link for more details about this property - https://www.mathworks.com/help/matlab/ref/inputparser.html#:~:text=KeepUnmatched%20%E2%80%94%20Matching%20indicator.
Here is some example code to illustrate this functionality
function p = vprintf(verbosity, varargin)
p = inputParser;
p.KeepUnmatched=true;
addOptional(p,'onNewLine', 0);
parse(p, varargin{:});
disp("Result property")
disp(p.Results)
disp("Unmatched property")
disp(p.Unmatched);
end
p = vprintf(1,"onNewLine", true, "aaaaaaaaaaa", 6);
Result property onNewLine: 1 Unmatched property aaaaaaaaaaa: 6
For additional reference please, refer to this MATLAB Answer post that discusses the “KeepUnmatched property in detail - https://www.mathworks.com/matlabcentral/answers/395295-allowing-unknown-parameters-in-an-inputparser.
Hope this helps!
  1 Commento
Bob Randall
Bob Randall il 28 Ott 2024 alle 20:12
Hello Raghave S N,
Unfortunately, the KeepUnmatched doesn't fix the problem completely. The code that you posted throws errors with the examples I have in my original post. Here is another example:
vprintf(1, "onNewLine" , true, "%d, %f, %s %d %d\n", 10, 123.4, "string", 1,2)
Error using vprintf (line 5)
Unmatched parameter name '%d, %f, %s %d %d\n' must be a string scalar or character
vector that can represent a field name.
It seems that the parser cannot process anything but name-value pairs arguments. I would add, given the above error, that the KeepUnmatched name choice is a little bit misleading. It should be something like CollectUnmatchedPairs. Anyway, I couldn't find a flag that allows to collect the parameters that cannot be parsed.
I wonder if the old matlab version, R2018a, I'm using matters in this case.
Thank you for your help.

Accedi per commentare.


Bob Randall
Bob Randall il 23 Nov 2024 alle 3:27
Hi Shlok,
Thank you for answering and providing a working solution to my problem. Right after submitting my initial post I decided to write my own parser function that does pretty much what yours does; here it is:
function [value, v] = argsParseNameValue(name, defaultValue, arg)
found = false;
v = {};
n = 1;
value = defaultValue;
while n <= length(arg)
if (isstring(arg{n}) || ischar(arg{n})) && arg{n} == name
if found
error("multiple declaration of "+name+" name-value argument.");
end
n = n+1;
value = arg{n};
found = true;
else
v = { v{:}, arg{n}}; %#ok<CCAT>
end
n = n+1;
end
end
It is not very efficient but does the job. And here is an example on how it can be used:
blah( 1000, "intArg", 10, 'stringArg', "assignedString", "boolArg", true);
1000 assignedString 12.3000 {["intArg"]} {[10]} {["boolArg"]} {[1]}
function blah(par, varargin)
[s , remainderArgs ] = argsParseNameValue("stringArg", "defaultString", varargin);
[x , remainderArgs ] = argsParseNameValue("doubleArg", 12.3 , remainderArgs);
disp(par)
disp(s)
disp(x)
disp(remainderArgs)
end
At the end, I found inputParser not that useful.
Thank you again , Shlok

Categorie

Scopri di più su Argument Definitions in Help Center e File Exchange

Community Treasure Hunt

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

Start Hunting!

Translated by