In GNU Octave, the frequently used plot function has several overloaded versions:

-- : plot (Y)
-- : plot (X, Y)
-- : plot (X, Y, FMT)
-- : plot (..., PROPERTY, VALUE, ...)
-- : plot (X1, Y1, ..., XN, YN)
-- : plot (HAX, ...)
-- : H = plot (...)

The arguments passed to plot have two main features as below, which make its function calling very flexible.

  1. The argument list has a variable length.
  2. After numeric parameters, such as HAX, X and Y, there are multiple of PROPERTY-VALUE pairs.

In my previous projects, when I wrote a function requiring different combinations of arguments, I would use varargin as its formal parameter, which wraps the list of input arguments in a cell array. Depending on the length of varargin, different parsing schemes were implemented.

function foo(varargin)
  switch (length(varargin))
    case 1:
    case 2:
    case 3:
  endswitch
endfunction

As an alternative, I might also explicitly list all the possible parameters in the function’s signature. Then, inside the function body, I used the exist function to check their availability and assigned default values to the missing variables.

function foo(image_handle, file_name, line_color)
  if (!exist("image_handle", "var"))
    ## Assign the default value to @p image_handle with the current figure
    ## handle.
    image_handle = gcf;
  endif

  if (!exist("file_name", "var"))
    ## Default image file for saving.
    file_name = "output.png";
  endif

  if (!exist("line_color", "var"))
    line_color = "r";
  endif
endfunction

It is obvious that the first method above requires lots of code to write, if there are many cases to be handled. For the second method, all the parameters are locked to their positions in the function’s signature, which is not flexible. Therefore, a new programming pattern is needed in order to implement a function similar to the internal function plot.

To solve this problem, Octave’s parseparams and inputParser class can be used. As shown in the following example code for plotting support points in a finite element cell, I pass varargin firstly to parseparams. It will separate the parameter list into two groups reg and prop. reg stores all the leading numeric arguments, while prop stores the remaining ones, the first of which should be a string. In my case, there is only one optional leading numeric parameter, which is the figure handle. Therefore, by checking the length of reg, figure handle can be obtained from the argument or set to the current figure via gcf.

Next, an inputParser object p is created for parsing and extracting both required parameters and parameter-value pairs. Note that each call of the parameter adding member function, like p.addRequired and p.addParameter, a function handle is passed as the last argument, which is responsible for user input validation. This is consistent with the spirit of defensive programming and makes our code more robust. Moreover, a default value should be provided for defining a parameter-value pair.

Finally, to run the parser p, call its member function parse on the unwrapped cell array prop{:}. The calling convention of plot_support_points now becomes consistent with plot.

function plot_support_points(varargin)
  ## The function @p parseparams is used for splitting the argument
  ## list into figure handle part and the remaining part.
  [reg, prop] = parseparams(varargin);

  if (length(reg) > 0)
    h = reg(1);
  else
    h = gcf;
  endif

  p = inputParser();
  p.FunctionName = "plot_support_points";
  p.addRequired("data_file", @ischar);
  val_float = @(x) isscalar(x) && isfloat(x);
  p.addParameter("offx", 0.02, val_float);
  p.addParameter("offy", 0.02, val_float);
  p.addParameter("marker", "o", @ischar);
  p.addParameter("markersize", 12, val_float);
  p.addParameter("markerfacecolor", "r", @ischar);
  p.addParameter("markeredgecolor", "r", @ischar);
  p.addParameter("labelsize", 18, val_float);

  p.parse(prop{:});
endfunction