File Exchange Pick of the Week

Coding challenge on input parsing 3

Posted by Jiro Doke,

Jiro's pick this week is boundedline by Kelly Kearney. Today, there's also a fun challenge at the end!

Contents

boundedline

Kelly is a veteran of the File Exchange, with a number of great entries, many of which have high ratings. I realized I had already picked one of her entries, legendflex. Going back and reading that post and looking through her code for legendflex, I realized why I like her entries. Kelly's programs are

  • well-written, with great help.
  • flexible, with many options.
  • robust, with good error checking.

Just type

help boundedline

and you'll see what I mean. boundedline is a nice function that allows you to plot lines with shaded bounds, that can represent error or confidence bounds.

She acknowledges a few other File Exchange entries, a couple of which have been picked previously as a Pick of the Week. boundedline has additional capabilities that make it unique.

Challenge

But today, I'd like to focus on one aspect of this entry, namely input parsing and error checking. As you can see from the help for the function, you can call boundedline in various ways:

[hl, hp] = boundedline(x, y, b)
[hl, hp] = boundedline(x, y, b, linespec)
[hl, hp] = boundedline(x1, y1, b1, linespec1, x2, y2, b2, linespec2)
[hl, hp] = boundedline(. . ., 'alpha')
[hl, hp] = boundedline(. . ., ax)
[hl, hp] = boundedline(. . ., 'transparency', trans)
[hl, hp] = boundedline(. . ., 'orientation', orient)
[hl, hp] = boundedline(. . ., 'cmap', cmap)

There are some required inputs, some optional inputs, and some optional parameter-value inputs. In order to make this work, Kelly parses the inputs and makes sure that the inputs are all valid. But she doesn't use inputParser or validateattributes. I know that she knows about these functions, because she used them in her other entry, legendflex. Perhaps she didn't know about them when she wrote boundedline.

So my challenge to everyone is to come up with some code that would do the appropriate input parsing and error checking on the inputs using inputParser. Feel free to use various error-checking functions, such as validateattributes.

Now, I haven't done this myself, but I can envision for the most part how I would go about doing this. However, I'm struggling with the 3rd calling syntax above. So I'm going to make that an extra credit. Here are the rules/requirements:

  1. The function should be able to handle all, except the 3rd, calling syntax above. Any number of the optional arguments can be passed in.
  2. The input parameters should meet the respective criteria. (see help boundedline)
  3. All optional inputs should have default values. (see help boundedline)
  4. You can assume that the parameter-value pair inputs will be called after the other optional inputs.
  5. After parsing, the parameters should be put into a structure with fields "x", "y", "b", "linespec", "usealpha", "hax", "trans", "orient", "cmap".
  6. (Extra credit) Allow for the 3rd calling syntax.

That's it! Put your thinking hat on, and post your solutions here to win some MATLAB swag! I will write another post highlighting some of the solutions I get.

Please post the code answer as a function that takes in varargin as an input and returns strct (with appropriate fields) as an output. Be sure to use the "code" markup so that your code will be easier to read:

<pre class="code">
funtionc strct = validateIntputs(varargin)
  ...
end
</pre>


Get the MATLAB code

Published with MATLAB® R2012b

3 CommentsOldest to Newest

Here’s my effort:

ipObj1 = inputParser;
ipObj1.addRequired('x',@(x)validateattributes(x,{'numeric'},{'finite','2d'}));
ipObj1.addRequired('y',@(x)validateattributes(x,{'numeric'},{'finite','2d'}));
ipObj1.addRequired('b',@(x)validateattributes(x,{'numeric'},{'finite'}));
keys = {'transparency','orientation','cmap'};
res = '';
res_ipObj2 = '';
res_ipObj3 = '';
try
    ipObj2 = ipObj1.createCopy;
    ipObj2.addOptional('linespec','',@(x)ischar(x) && ~any(strcmp(x,{'alpha',keys{:}})));
    ipObj2.addOptional('useAlpha','',@(x)strcmp(x,'alpha') || ishandle(x));
    ipObj2.addOptional('hax','',@(x)strcmp(x,'alpha') || ishandle(x));
    ipObj2.addParamValue('orientation','horiz',@(x)any(validatestring(x,{'horiz','vert'})));
    ipObj2.addParamValue('transparency',0.2,@(x)validateattributes(x,{'numeric'},{'scalar','positive'}));
    ipObj2.addParamValue('cmap',[],@(x)validateattributes(x,{'numeric'},{'2d','finite','size',[size(x,1),3]}));
    ipObj2.parse(varargin{:});
    res_ipObj2 = ipObj2.Results;
    res = res_ipObj2;
catch me
    try 
        ipObj3 = ipObj1.createCopy;
        ipObj3.addOptional('linespec','',@(x)ischar(x) && ~any(strcmp(x,{'alpha',keys{:}})));
        ipObj3.addOptional('x1',[],@(x)validateattributes(x,{'numeric'},{'finite','2d'}));
        ipObj3.addOptional('y2',[],@(x)validateattributes(x,{'numeric'},{'finite','2d'}));
        ipObj3.addOptional('b2',[],@(x)validateattributes(x,{'numeric'},{'finite'}));
        ipObj3.addOptional('linespec2','',@(x)ischar(x) && ~any(strcmp(x,{'alpha',keys{:}})));
        ipObj3.addOptional('useAlpha','',@(x)strcmp(x,'alpha') || ishandle(x));
        ipObj3.addOptional('hax','',@(x)strcmp(x,'alpha') || ishandle(x));
        ipObj3.addParamValue('orientation','horiz',@(x)any(validatestring(x,{'horiz','vert'})));
        ipObj3.addParamValue('transparency',0.2,@(x)validateattributes(x,{'numeric'},{'scalar','positive'}));
        ipObj3.addParamValue('cmap',[],@(x)validateattributes(x,{'numeric'},{'2d','finite','size',[size(x,1),3]}));
        ipObj3.parse(varargin{:})
        res_ipObj3 = ipObj3.Results;
        res = res_ipObj3;
        
    catch me2%345345
        res_ipObj3 = '';
    end
    res_ipObj2 = '';
end
if isempty(res_ipObj2) && isempty(res_ipObj3)
    try
        ipObj4 = ipObj1.createCopy;
        ipObj4.addOptional('useAlpha','',@(x)strcmp(x,'alpha') || ishandle(x));
        ipObj4.addOptional('hax','',@(x)strcmp(x,'alpha') || ishandle(x));
        ipObj4.addParamValue('orientation','horiz',@(x)any(validatestring(x,{'horiz','vert'})));
        ipObj4.addParamValue('transparency',0.2,@(x)validateattributes(x,{'numeric'},{'scalar','positive'}));
        ipObj4.addParamValue('cmap',[],@(x)validateattributes(x,{'numeric'},{'2d','finite','size',[size(x,1),3]}));
        ipObj4.addOptional('linespec','',@(x)ischar(x) && ~any(strcmp(x,{'alpha',keys{:}})));
        ipObj4.parse(varargin{:});
        res = ipObj4.Results;
    catch me
        error(me.identifier,me.message)
    end
end
if ishandle(res.useAlpha)
    tmp1 = res.useAlpha;
    tmp2 = res.hax;
    res.hax = tmp1;
    res.useAlpha = tmp2;
end

Thanks Mikko for your entry! I will parse through this and write up an entry on this.

Let me send you an email to get your contact info so that we can send you some MathWorks gifts!

These postings are the author's and don't necessarily represent the opinions of MathWorks.