Warm tip: This article is reproduced from serverfault.com, please click

Does Matlab's new function argument validation allow for unmatched name-value arguments?

发布于 2020-11-28 05:49:47

Tl; Dr: Is there a way to have unmatched optional name-value arguments in Matlab's new (2019b) function argument validation?

I have existing Matlab code that uses an input parser to dispatch optional arguments to my sim. The sim is set up such that you choose a few functions that dictate how your object behaves in its environment, and you call runSim. In this imaginary example, my sim is being defined with two functions that take both unique and common optional inputs.

runSim('sharedArg', sa, 'func1Arg1', f1a1, 'func1Arg1', f1a2, 'func2Arg1', f2a1)

runSim will call the appropriate functions that you chose to define your sim, and pass those optional arguments along to each function. In this case, the input parser in func1 will ignore func2arg1 and the input parser in func2 will ignore func1Arg1 and func1Arg2.

Everything works great, but my sim spends a third of its time doing input parsing, as these functions are called thousands of times in a loop. And Matlab's input parser has a documented history of being slow as (insert profanity here).

Having recently updated to 2020a, I have discovered function argument validation, which is a lot less terrible than input parsers. Having tested it out on a few of much functions, not only is the code much more readable, but I'm seeing a huge boost in performance.

function output = func1FirstTry(options)
    % This function does some things
    arguments
        options.sharedArg
        options.func1Arg1 double = 1
        options.func1Arg2 double = 2
    end
    output = 2;
end

Love it. Great. Awesome. BUT...

... the new function validation does not allow for unmatched arguments (or at least the linked page doesn't explain it, and I haven't found any more thorough documentation). Previously, func1 only had to know about its optional arguments. If I specified func2arg1 as an input to func1, it would just ignore it. With the new function validation, this will throw an error, as I have not defined func2Arg1 as an allowed input in the arguments block. So when I did this test, I had to instead do the following:

function output = func1SecondTry(options)
    % This function does some things
    arguments
        options.sharedArg
        options.func1Arg1 double = 1
        options.func1Arg2 double = 2
        options.func2Arg1 double = Nan
    end
    % Rest of the code goes here
end

Now this works, but I also have to change func2 to accept func1's optional arguments. And I have like 20+ more functions with optional arguments to consider, so obviously this strategy is not going to work out. Is there a way I can specify that I want to accept and ignore any unnamed optional inputs in the same manner that input parser would? Specifically, I would like func1FirstTry('func2Arg1', 3) to not error, without adding func2Arg1 to the arguments block.

Questioner
James Grammatikos
Viewed
0
James Grammatikos 2021-01-04 15:17:29

This ended up not being the solution that I took, but I think a possible answer is to define a dummy class with all the possible inputs to the parser as public properties, then use the values from class properties syntax. So all of the properties of that class are legal inputs to the functions, but then you would only access the parameters specific to that function. This syntax also allows you to redefine any specific parameters if you want to change the default values.

% Define this in its own file
classdef OptionalArgumentClass
    properties
        sharedArg
        func1Arg1
        func1Arg2
        func2Arg1
        argumentUndefinedByOtherFunctions
    end
end
% In a separate file from class
function output = func1(options)
    % This function does some things
    arguments
        options.?Path.To.OptionalArgumentClass
        options.func1Arg1 = 1 % Can choose specific default values here if needed
    end

    % options.func1Arg2 has no default value, so this will error if unspecified
    output = options.func1Arg1 + options.func1Arg2;
end

The inclusion of options.?Path.To.OptionalArgumentClass means that I can specify func2Arg1 or argumentUndefinedByOtherFunctions and have them be gracefully ignored, which was all that I was looking for.