Loren on the Art of MATLAB

Turn ideas into MATLAB

Note

Loren on the Art of MATLAB has been archived and will not be updated.

Time for an Argument

For those Monty Python fans reading this post, check out the Argument Clinic. Recently both Sean and Jiro, twice! posted about the new arguments capability available for input argument checking. I wanted to show you some more about this great feature! I've also discussed argument checking over the years, including this post.

Contents

What Makes a Program Good, or Useful?

Of course, this in the eye of the user frequently, but certainly it helps if you get informative messages when you misuse the program. These messages should help guide you so you can properly use the program without a major headache. To do so generally requires some amount of error checking on the part of the programmer, thinking about what can go wrong, and making sure to give good information if/when it does.

I'm going to show you some code snippets from a routine. The function in question is modeling the dynamics of a virus, given parameters such as the infection rate.

Original error checking, circa 2007

In this first code, I will show you how we did some important error checking about 12 years ago. In 35 lines, we check to see if the user called the function this code is embedded in with 0, 1, or 5 inputs. And we check the types of the inputs. If not all the inputs are supplied by the user, we set the unsupplied variables with default values.

%  Allowing for variable number of arguments in
   switch(nargin)
       case 5
           Tpoints = varargin{1};  % time to evaluate at
           beta    = varargin{2};  % infection rate
           c       = varargin{3};  % clearance rate of virus
           delta   = varargin{4};  % death rate of infected cells
           p       = varargin{5};  % viral production rate of infectious cells
       case 1
           Tpoints = varargin{1};  % time to evaluate at
           beta    = 3.4e-5;       % infection rate
           c       = 3.3;          % clearance rate of virus
           delta   = 3.4;          % death rate of infected cells
           p       = 7.9e-3;       % viral production rate of infectious cells
       case 0
           % Use all default values
           Tpoints = 0:0.1:10;     % time to evaluate at
           beta    = 3.4e-5;       % infection rate
           c       = 3.3;          % clearance rate of virus
           delta   = 3.4;          % death rate of infected cells
           p       = 7.9e-3;       % viral production rate of infectious cells
       otherwise
           error('virusSolve:Input:InvalidNumberOfInputs', ...
               'This function expects 0, 1, or 5 inputs.');
   end
%  Error checking - make sure all inputs are valid
   validateattributes(Tpoints, {'numeric'}, {'vector', 'nonnegative'},mfilename,'Tpoints',1);
   validateattributes(beta   , {'numeric'}, {'scalar', 'real'});
   validateattributes(c      , {'numeric'}, {'scalar', 'real'});
   validateattributes(delta  , {'numeric'}, {'scalar', 'real'});
   validateattributes(p      , {'numeric'}, {'scalar', 'real'});

You can see heavy use of validateattributes as well as the use of nargin, and switch, case, otherwise.

Another solution, similar time frame

There was another solution availabe in about the same time frame using a function called inputParser. Here's what that error checking code looks like.

%  Create input parser
   ps = inputParser();
   % Define optional arguments with default values and error checking
   ps.addOptional('Tpoints', 0:0.1:10, ...
       @(x) validateattributes(x, {'numeric'}, {'vector', 'nonnegative'}));
   ps.addOptional('beta', 3.4e-5, ...
       @(x) validateattributes(x, {'numeric'}, {'scalar', 'real'}));
   ps.addOptional('c', 3.3, ...
       @(x) validateattributes(x, {'numeric'}, {'scalar', 'real'}));
   ps.addOptional('delta', 3.4, ...
       @(x) validateattributes(x, {'numeric'}, {'scalar', 'real'}));
   ps.addOptional('p', 7.9e-3, ...
       @(x) validateattributes(x, {'numeric'}, {'scalar', 'real'}));
%  Parse
   parse(ps, varargin{:});
% Extract parameters
   Tpoints = ps.Results.Tpoints;
   beta    = ps.Results.beta;
   c       = ps.Results.c;
   delta   = ps.Results.delta;
   p       = ps.Results.p;

A bit better if you count lines, we're down to 25, from 35. In this case, you see that the limitation of number of input arguments is relaxed. It can go from 0 to 5 now.

And the modern way, circa 2019

In MATLAB release 2019b, we introduced a new way to perform input argument validation, using the arguments block at the beginning of the function code. Here's how to do what we've previously accomplished in 35, then 25 lines of code.

   arguments % R2019b or newer
       Tpoints(1, :) double {mustBeNumeric, mustBeNonnegative} = 0:0.1:10
       beta(1, 1) double {mustBeReal} = 3.4e-5
       c(1, 1) double {mustBeReal} = 3.3
       delta(1, 1) double {mustBeReal} = 3.4
       p(1, 1) double {mustBeReal} = 7.9e-3
   end

In 7 lines of code, you can see that we specify the conditions we want for each input, and supply a default value if the input is missing.

This is just a simple example. There is a lot more fodder you can bring to arguments. Check out the documentation to see the possibilities.

Do you have arguments?

How do you resolve your arguments? Let me know here.




Published with MATLAB® R2019b


  • print