Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MATLAB: Using inputParser with varargin

Tags:

matlab

I have a function for which I would like to pass in arguments via varargin, and use the inputParser to ensure the inputs are sane. Some arguments are required, and some are not. Here is an example:

function myFunc(varargin)
    p = inputParser;
    p.addRequired(...
            'numStates', ...
                @(x) validateattributes(x, {'numeric'}, ...
                {'scalar', 'integer', 'positive'}));
        p.addRequired(...
            'numInps', ...
                @(x) validateattributes(x, {'numeric'}, ...
                {'scalar', 'integer', 'positive'}));
        p.addRequired(...
            'numOuts', ...
                @(x) validateattributes(x, {'numeric'}, ...
                {'scalar', 'integer', 'positive'}));
        p.addRequired(...
            'X0', ...
                @(x) validateattributes(x, {'numeric'}, ...
                {'vector'}));
        p.addOptional(...
            'freq', 10, ...
                @(x) validateattributes(x, {'numeric'}, ...
                {'scalar', 'integer', 'positive'}));
        p.addOptional(...
            'SimulinkVariables', struct(), ...
                @(x) isa(x, 'struct'));

    p.parse(varargin{:});

    %# do stuff with variables
end

I would like to be able to pass in arguments as follows; it should not matter which pair gets passed in when, as long as the required ones are there. So a sample call might be:

myFunc('numStates', 4, 'numInps', 2, 'numOUts', 3, 'X0', [4;0]);

Apparently, this syntax is illegal; parse() expects that the first arguments in it are the required values, but they should not be explicitly named, i.e., as in:

function myFunc(numStates, numInps, numOuts, X0, varargin)
    ...
    p.parse(numStates, numInps, numOuts, X0, varargin{:});

end

Is there a simple way to make this do what I want, i.e., the first function? I guess the easiest thing to do is do something to reorder the elements of varargin and kick out the argument names, but that isn't terribly elegant.

like image 478
Dang Khoa Avatar asked Dec 17 '22 04:12

Dang Khoa


2 Answers

The most elegant solution I can think of is to subclass inputParser. So you could do something like this (save as myInputParser.m):

classdef myInputParser < inputParser
    properties
        required = {};
    end

    methods
        function obj = myInputParser
            obj = obj@inputParser;
        end

        function addRequired(obj, argname, validator)
            obj.required = {obj.required{:}, argname};
            obj.addOptional(argname, [], validator);
        end

        function parse(obj, varargin)
            params_input = varargin(1:2:end);
            for i=1:length(obj.required)
                if isempty(strmatch(obj.required{i}, params_input))
                    error(sprintf('Required named parameter %s was not passed to function', obj.required{i}));
                end
            end
            parse@inputParser(obj, varargin{:});
        end

    end
end

Then change

    p = inputParser;

to

    p = myInputParser;

and then your code will work as you want it to.

Note that this might not handle all edge cases correctly, and I haven't tested it extensively, but it does work with your example use case.

like image 57
dumbmatter Avatar answered Jan 05 '23 03:01

dumbmatter


In InputParser you can add ParameterName - ParameterValue pairs only with addParamValue. Those arguments are supposed to be optional.

As a workaround you can add all your parameters with addParamValue and use [] as default value for required arguments.

Then you can do one of the following:

  • add 'nonempty' to attributes in validateattributes function for required arguments (however in this case the error message will not say this is required arguments, but that it has to be non-empty)
  • or add separate check (with if or assert) that required arguments should be not-empty with your own error message.

You are not limited to use []. The default can be any value not appropriate for a particular parameter and easy to check.

like image 39
yuk Avatar answered Jan 05 '23 05:01

yuk