{"id":4389,"date":"2013-02-22T09:00:41","date_gmt":"2013-02-22T14:00:41","guid":{"rendered":"https:\/\/blogs.mathworks.com\/pick\/?p=4389"},"modified":"2013-02-21T19:31:11","modified_gmt":"2013-02-22T00:31:11","slug":"coding-challenge-on-input-parsing-result","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/pick\/2013\/02\/22\/coding-challenge-on-input-parsing-result\/","title":{"rendered":"Coding challenge on input parsing &#8211; Result"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <introduction>\r\n      <p>This week, <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/15007\">Jiro<\/a> is highlighting a code snippet submitted by Mikko Lepp&auml;nen.\r\n      <\/p>\r\n      <p>In my previous <a title=\"https:\/\/blogs.mathworks.com\/pick\/2013\/02\/01\/coding-challenge-on-input-parsing\/ (link no longer works)\">post<\/a>, I posed a coding challenge to come up with an input validation code snippet to work with <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/27485-boundedline-m\"><tt>boundedline<\/tt><\/a> by <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/287218\">Kelly<\/a>. The function is a well-written piece of code with flexible input arguments. Here are the different ways you could call the\r\n         function:\r\n      <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">[hl, hp] = boundedline(x, y, b)\r\n[hl, hp] = boundedline(x, y, b, linespec)\r\n[hl, hp] = boundedline(x1, y1, b1, linespec1, x2, y2, b2, linespec2)\r\n[hl, hp] = boundedline(. . ., <span style=\"color: #A020F0\">'alpha'<\/span>)\r\n[hl, hp] = boundedline(. . ., ax)\r\n[hl, hp] = boundedline(. . ., <span style=\"color: #A020F0\">'transparency'<\/span>, trans)\r\n[hl, hp] = boundedline(. . ., <span style=\"color: #A020F0\">'orientation'<\/span>, orient)\r\n[hl, hp] = boundedline(. . ., <span style=\"color: #A020F0\">'cmap'<\/span>, cmap)<\/pre><p>Here were the ground rules for the challenge:<\/p>\r\n      <div>\r\n         <ol>\r\n            <li>The function should be able to handle all, except the 3rd, calling syntax above. Any number of the optional arguments can\r\n               be passed in.\r\n            <\/li>\r\n            <li>The input parameters should meet the respective criteria. (see help boundedline)<\/li>\r\n            <li>All optional inputs should have default values. (see help boundedline)<\/li>\r\n            <li>You can assume that the parameter-value pair inputs will be called after the other optional inputs.<\/li>\r\n            <li>After parsing, the parameters should be put into a structure with fields \"x\", \"y\", \"b\", \"linespec\", \"usealpha\", \"hax\", \"trans\",\r\n               \"orient\", \"cmap\".\r\n            <\/li>\r\n            <li>(Extra credit) Allow for the 3rd calling syntax.<\/li>\r\n         <\/ol>\r\n      <\/div>\r\n      <p>Mikko was the only person who rose to the challenge, and it was great! His solution follows the ground rules, <i>and he even did the extra credit!<\/i> I wrote my own as well, but after seeing Mikko's solution, I was able to improve mine. Today I am going to dissect both of\r\n         our codes and explain in detail.\r\n      <\/p>\r\n   <\/introduction>\r\n   <h3>Contents<\/h3>\r\n   <div>\r\n      <ul>\r\n         <li><a href=\"#1\">Mikko's Solution<\/a><\/li>\r\n         <li><a href=\"#12\">My Solution<\/a><\/li>\r\n         <li><a href=\"#16\">Comments<\/a><\/li>\r\n      <\/ul>\r\n   <\/div>\r\n   <h3>Mikko's Solution<a name=\"1\"><\/a><\/h3>\r\n   <p>Rather than creating a single <tt>inputParser<\/tt>, Mikko makes use of <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/try.html\"><tt>try\/catch<\/tt><\/a> statements to deal with multiple scenarios. Let's take a look. <i>Note: I made some minor modifications to his code for this post.<\/i><\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #0000FF\">function<\/span> res = validateInputs(varargin)<\/pre><p><b>1.<\/b> First, an <a title=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/inputparserclass.html (link no longer works)\"><tt>inputParser<\/tt><\/a> object is created. Since there are 3 required inputs <tt>x<\/tt>, <tt>y<\/tt>, and <tt>b<\/tt>, they are added to the <tt>inputParser<\/tt> object using the <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/inputparser.addrequired.html\"><tt>addRequired<\/tt><\/a> method. Note that <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/validateattributes.html\"><tt>validateattributes<\/tt><\/a> makes sure that the variables are numeric, finite, and 2d.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">ipObj1 = inputParser;\r\nipObj1.addRequired(<span style=\"color: #A020F0\">'x'<\/span>, @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, {<span style=\"color: #A020F0\">'finite'<\/span>, <span style=\"color: #A020F0\">'2d'<\/span>}));\r\nipObj1.addRequired(<span style=\"color: #A020F0\">'y'<\/span>, @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, {<span style=\"color: #A020F0\">'finite'<\/span>, <span style=\"color: #A020F0\">'2d'<\/span>}));\r\nipObj1.addRequired(<span style=\"color: #A020F0\">'b'<\/span>, @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, {<span style=\"color: #A020F0\">'finite'<\/span>}));<\/pre><p><b>2.<\/b> Define optional parameters that could be passed.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">keys = {<span style=\"color: #A020F0\">'transparency'<\/span>, <span style=\"color: #A020F0\">'orientation'<\/span>, <span style=\"color: #A020F0\">'cmap'<\/span>};<\/pre><p><b>3.<\/b> First try: consider a case where optional parameters <tt>linespec<\/tt>, <tt>useAlpha<\/tt>, and <tt>hax<\/tt> have been passed. Note that the validation for <tt>useAlpha<\/tt> and <tt>hax<\/tt> is checking for either the string \"alpha\" or a handle. This allows the two optional values to be passed in any order. See\r\n      step 6, where it puts the parameters in the correct order. Following the optional parameters, the three parameter-value pairs\r\n      are defined.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #0000FF\">try<\/span><\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">  ipObj2 = ipObj1.createCopy;\r\n  ipObj2.addOptional(<span style=\"color: #A020F0\">'linespec'<\/span>, <span style=\"color: #A020F0\">''<\/span>, @(x)ischar(x) &amp;&amp; ~any(strcmp(x, <span style=\"color: #0000FF\">...<\/span>\r\n                     [{<span style=\"color: #A020F0\">'alpha'<\/span>}, keys])));\r\n  ipObj2.addOptional(<span style=\"color: #A020F0\">'useAlpha'<\/span>, <span style=\"color: #A020F0\">''<\/span>, @(x)strcmp(x, <span style=\"color: #A020F0\">'alpha'<\/span>) || ishandle(x));\r\n  ipObj2.addOptional(<span style=\"color: #A020F0\">'hax'<\/span>, gca, @(x)strcmp(x, <span style=\"color: #A020F0\">'alpha'<\/span>) || ishandle(x));\r\n  ipObj2.addParamValue(<span style=\"color: #A020F0\">'orientation'<\/span>, <span style=\"color: #A020F0\">'horiz'<\/span>, <span style=\"color: #0000FF\">...<\/span>\r\n                       @(x)any(validatestring(x, {<span style=\"color: #A020F0\">'horiz'<\/span>, <span style=\"color: #A020F0\">'vert'<\/span>})));\r\n  ipObj2.addParamValue(<span style=\"color: #A020F0\">'transparency'<\/span>, 0.2, @(x)validateattributes(x, <span style=\"color: #0000FF\">...<\/span>\r\n                       {<span style=\"color: #A020F0\">'numeric'<\/span>}, {<span style=\"color: #A020F0\">'scalar'<\/span>, <span style=\"color: #A020F0\">'positive'<\/span>}));\r\n  ipObj2.addParamValue(<span style=\"color: #A020F0\">'cmap'<\/span>, [], @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, <span style=\"color: #0000FF\">...<\/span>\r\n                       {<span style=\"color: #A020F0\">'2d'<\/span>, <span style=\"color: #A020F0\">'finite'<\/span>, <span style=\"color: #A020F0\">'size'<\/span>, [size(x, 1), 3]}));\r\n\r\n  ipObj2.parse(varargin{:});\r\n  res = ipObj2.Results;<\/pre><p><b>4.<\/b> If the above parsing fails, try a second parser. In this scenario, it is checking to see if the user supplied a second set\r\n      of data for plotting - <tt>x2<\/tt>, <tt>y2<\/tt>, <tt>b2<\/tt>. <i>Note that this is the extra credit!<\/i><\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #0000FF\">catch<\/span> me\r\n  <span style=\"color: #0000FF\">try<\/span><\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">    ipObj3 = ipObj1.createCopy;\r\n    ipObj3.addOptional(<span style=\"color: #A020F0\">'linespec'<\/span>, <span style=\"color: #A020F0\">''<\/span>, @(x)ischar(x) &amp;&amp; ~any(strcmp(x, <span style=\"color: #0000FF\">...<\/span>\r\n                       [{<span style=\"color: #A020F0\">'alpha'<\/span>}, keys])));\r\n    ipObj3.addOptional(<span style=\"color: #A020F0\">'x2'<\/span>, [], @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, <span style=\"color: #0000FF\">...<\/span>\r\n                       {<span style=\"color: #A020F0\">'finite'<\/span>, <span style=\"color: #A020F0\">'2d'<\/span>}));\r\n    ipObj3.addOptional(<span style=\"color: #A020F0\">'y2'<\/span>, [], @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, <span style=\"color: #0000FF\">...<\/span>\r\n                       {<span style=\"color: #A020F0\">'finite'<\/span>, <span style=\"color: #A020F0\">'2d'<\/span>}));\r\n    ipObj3.addOptional(<span style=\"color: #A020F0\">'b2'<\/span>, [], @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, <span style=\"color: #0000FF\">...<\/span>\r\n                       {<span style=\"color: #A020F0\">'finite'<\/span>}));\r\n    ipObj3.addOptional(<span style=\"color: #A020F0\">'linespec2'<\/span>, <span style=\"color: #A020F0\">''<\/span>, @(x)ischar(x) &amp;&amp; ~any(strcmp(x, <span style=\"color: #0000FF\">...<\/span>\r\n                       [{<span style=\"color: #A020F0\">'alpha'<\/span>}, keys])));\r\n    ipObj3.addOptional(<span style=\"color: #A020F0\">'useAlpha'<\/span>, <span style=\"color: #A020F0\">''<\/span>, @(x)strcmp(x, <span style=\"color: #A020F0\">'alpha'<\/span>) || ishandle(x));\r\n    ipObj3.addOptional(<span style=\"color: #A020F0\">'hax'<\/span>, gca, @(x)strcmp(x, <span style=\"color: #A020F0\">'alpha'<\/span>) || ishandle(x));\r\n    ipObj3.addParamValue(<span style=\"color: #A020F0\">'orientation'<\/span>, <span style=\"color: #A020F0\">'horiz'<\/span>, @(x)any(validatestring(x, <span style=\"color: #0000FF\">...<\/span>\r\n                         {<span style=\"color: #A020F0\">'horiz'<\/span>, <span style=\"color: #A020F0\">'vert'<\/span>})));\r\n    ipObj3.addParamValue(<span style=\"color: #A020F0\">'transparency'<\/span>, 0.2, @(x)validateattributes(x, <span style=\"color: #0000FF\">...<\/span>\r\n                         {<span style=\"color: #A020F0\">'numeric'<\/span>}, {<span style=\"color: #A020F0\">'scalar'<\/span>, <span style=\"color: #A020F0\">'positive'<\/span>}));\r\n    ipObj3.addParamValue(<span style=\"color: #A020F0\">'cmap'<\/span>, [], @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, <span style=\"color: #0000FF\">...<\/span>\r\n                         {<span style=\"color: #A020F0\">'2d'<\/span>, <span style=\"color: #A020F0\">'finite'<\/span>, <span style=\"color: #A020F0\">'size'<\/span>, [size(x, 1), 3]}));\r\n\r\n    ipObj3.parse(varargin{:});\r\n    res = ipObj3.Results;<\/pre><p><b>5.<\/b> If the above parsing fails, try a third parser. In this scenario, it is checking to see if the user omitted the <tt>linespec<\/tt> parameter.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">  <span style=\"color: #0000FF\">catch<\/span> me2\r\n    ipObj4 = ipObj1.createCopy;\r\n    ipObj4.addOptional(<span style=\"color: #A020F0\">'useAlpha'<\/span>, <span style=\"color: #A020F0\">''<\/span>, @(x)strcmp(x, <span style=\"color: #A020F0\">'alpha'<\/span>) || ishandle(x));\r\n    ipObj4.addOptional(<span style=\"color: #A020F0\">'hax'<\/span>, gca, @(x)strcmp(x, <span style=\"color: #A020F0\">'alpha'<\/span>) || ishandle(x));\r\n    ipObj4.addParamValue(<span style=\"color: #A020F0\">'orientation'<\/span>, <span style=\"color: #A020F0\">'horiz'<\/span>, @(x)any(validatestring(x, <span style=\"color: #0000FF\">...<\/span>\r\n                         {<span style=\"color: #A020F0\">'horiz'<\/span>, <span style=\"color: #A020F0\">'vert'<\/span>})));\r\n    ipObj4.addParamValue(<span style=\"color: #A020F0\">'transparency'<\/span>, 0.2, @(x)validateattributes(x, <span style=\"color: #0000FF\">...<\/span>\r\n                         {<span style=\"color: #A020F0\">'numeric'<\/span>}, {<span style=\"color: #A020F0\">'scalar'<\/span>, <span style=\"color: #A020F0\">'positive'<\/span>}));\r\n    ipObj4.addParamValue(<span style=\"color: #A020F0\">'cmap'<\/span>, [], @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, <span style=\"color: #0000FF\">...<\/span>\r\n                         {<span style=\"color: #A020F0\">'2d'<\/span>, <span style=\"color: #A020F0\">'finite'<\/span>, <span style=\"color: #A020F0\">'size'<\/span>, [size(x, 1), 3]}));\r\n    ipObj4.addOptional(<span style=\"color: #A020F0\">'linespec'<\/span>, <span style=\"color: #A020F0\">''<\/span>, @(x)ischar(x) &amp;&amp; ~any(strcmp(x, <span style=\"color: #0000FF\">...<\/span>\r\n                       [{<span style=\"color: #A020F0\">'alpha'<\/span>}, keys])));\r\n\r\n    ipObj4.parse(varargin{:});\r\n    res = ipObj4.Results;\r\n  <span style=\"color: #0000FF\">end<\/span>\r\n<span style=\"color: #0000FF\">end<\/span><\/pre><p><b>6.<\/b> If it passed all of the parsing above, <tt>res<\/tt> has the necessary parameters. However, since <tt>useAlpha<\/tt> and <tt>hax<\/tt> could have been passed in either order, Mikko checks to see if <tt>useAlpha<\/tt> is a handle or not. If it is, he swaps <tt>useAlpha<\/tt> and <tt>hax<\/tt>.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #0000FF\">if<\/span> ishandle(res.useAlpha)\r\n  tmp1 = res.useAlpha;\r\n  tmp2 = res.hax;\r\n  res.hax = tmp1;\r\n  res.useAlpha = tmp2;\r\n<span style=\"color: #0000FF\">end<\/span><\/pre><p><b>7.<\/b> Finally, since <tt>useAlpha<\/tt> needs to be a logical (true or false), it is set to true if it has the string \"alpha\". If not, it is set to false.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #0000FF\">if<\/span> isequal(res.useAlpha, <span style=\"color: #A020F0\">'alpha'<\/span>)\r\n  res.useAlpha = true;\r\n<span style=\"color: #0000FF\">else<\/span>\r\n  res.useAlpha = false;\r\n<span style=\"color: #0000FF\">end<\/span><\/pre><h3>My Solution<a name=\"12\"><\/a><\/h3>\r\n   <p>I also have a solution that I was able to improve based on a technique I saw in Mikko's solution. I like the flexibility he\r\n      introduced in the optional arguments \"usealpha\" and \"hax\", where the user could provide those inputs in either order. I've\r\n      extended it to work for all 3 optional arguments \"linespec\", \"usealpha\" and \"hax\". Here's what I have. For this solution,\r\n      I'm ignoring the \"extra credit\" part of the challenge.\r\n   <\/p>\r\n   <p>I have a custom validation function <tt>validateOptionalArgs<\/tt> for the 3 optional arguments \"linespec\", \"usealpha\", and \"hax\".\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">p = inputParser;\r\np.addRequired(<span style=\"color: #A020F0\">'x'<\/span>, @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, {<span style=\"color: #A020F0\">'finite'<\/span>, <span style=\"color: #A020F0\">'2d'<\/span>}));\r\np.addRequired(<span style=\"color: #A020F0\">'y'<\/span>, @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, {<span style=\"color: #A020F0\">'finite'<\/span>, <span style=\"color: #A020F0\">'2d'<\/span>}));\r\np.addRequired(<span style=\"color: #A020F0\">'b'<\/span>, @(x)validateattributes(x, {<span style=\"color: #A020F0\">'numeric'<\/span>}, {<span style=\"color: #A020F0\">'finite'<\/span>}));\r\np.addOptional(<span style=\"color: #A020F0\">'linespec'<\/span>, [], @(x) validateOptionalArgs(x) &gt; 0);\r\np.addOptional(<span style=\"color: #A020F0\">'usealpha'<\/span>, [], @(x) validateOptionalArgs(x) &gt; 0);\r\np.addOptional(<span style=\"color: #A020F0\">'hax'<\/span>     , [], @(x) validateOptionalArgs(x) &gt; 0);\r\np.addParamValue(<span style=\"color: #A020F0\">'orientation'<\/span>, <span style=\"color: #A020F0\">'horiz'<\/span>, @(x) ismember(x, {<span style=\"color: #A020F0\">'vert'<\/span>, <span style=\"color: #A020F0\">'horiz'<\/span>}));\r\np.addParamValue(<span style=\"color: #A020F0\">'transparency'<\/span>, 0.2, @(x) validateattributes(x, <span style=\"color: #0000FF\">...<\/span>\r\n                {<span style=\"color: #A020F0\">'numeric'<\/span>}, {<span style=\"color: #A020F0\">'scalar'<\/span>, <span style=\"color: #A020F0\">'&gt;='<\/span>, 0, <span style=\"color: #A020F0\">'&lt;='<\/span>, 1}));\r\np.addParamValue(<span style=\"color: #A020F0\">'cmap'<\/span>, colormap(gca), @(x) validateattributes(x, <span style=\"color: #0000FF\">...<\/span>\r\n                {<span style=\"color: #A020F0\">'double'<\/span>}, {<span style=\"color: #A020F0\">'size'<\/span>, [NaN, 3], <span style=\"color: #A020F0\">'&gt;='<\/span>, 0, <span style=\"color: #A020F0\">'&lt;='<\/span>, 1}));\r\n\r\np.parse(varargin{:});\r\nres = p.Results;<\/pre><p>Since \"linespec\", \"usealpha\", and \"hax\" could be passed in any order, map them back to the true parameters.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #228B22\">% Default values<\/span>\r\nlinespec = <span style=\"color: #A020F0\">''<\/span>;\r\nusealpha = false;\r\nhax = gca;\r\n\r\n<span style=\"color: #0000FF\">for<\/span> iF = {<span style=\"color: #A020F0\">'linespec'<\/span>, <span style=\"color: #A020F0\">'usealpha'<\/span>, <span style=\"color: #A020F0\">'hax'<\/span>}\r\n  field = iF{1};\r\n\r\n  <span style=\"color: #0000FF\">switch<\/span> validateOptionalArgs(res.(field))\r\n    <span style=\"color: #0000FF\">case<\/span> 1\r\n      linespec = res.(field);\r\n    <span style=\"color: #0000FF\">case<\/span> 2\r\n      usealpha = res.(field);\r\n    <span style=\"color: #0000FF\">case<\/span> 3\r\n      hax = res.(field);\r\n    <span style=\"color: #0000FF\">case<\/span> 0\r\n      <span style=\"color: #228B22\">% skip<\/span>\r\n  <span style=\"color: #0000FF\">end<\/span>\r\n<span style=\"color: #0000FF\">end<\/span>\r\n\r\n<span style=\"color: #228B22\">% Map it back<\/span>\r\n[res.linespec, res.usealpha, res.hax] = deal(linespec, usealpha, hax);<\/pre><p>Finally, I set \"usealpha\" to TRUE if it has the string \"alpha\".<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #0000FF\">if<\/span> isequal(res.usealpha, <span style=\"color: #A020F0\">'alpha'<\/span>)\r\n  res.usealpha = true;\r\n<span style=\"color: #0000FF\">end<\/span><\/pre><p>Here's my <tt>validateOptionalArgs<\/tt> function which checks to see if the argument matches one of the 3 optional inputs. It returns which option it matched. If\r\n      it did not match any, it returns 0.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #0000FF\">function<\/span> s = validateOptionalArgs(x)<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #228B22\">% s = validateOptionalArgs(x)<\/span>\r\n<span style=\"color: #228B22\">%<\/span>\r\n<span style=\"color: #228B22\">%   Returns 0, 1, 2, 3<\/span>\r\n<span style=\"color: #228B22\">%       0 - did not match any<\/span>\r\n<span style=\"color: #228B22\">%       1 - matched linespec<\/span>\r\n<span style=\"color: #228B22\">%       2 - matched 'alpha'<\/span>\r\n<span style=\"color: #228B22\">%       3 - matched hax<\/span>\r\n\r\ntf = false;\r\n\r\n<span style=\"color: #228B22\">% First, check to see if it's a valid LineSpec<\/span>\r\n<span style=\"color: #0000FF\">if<\/span> ischar(x)\r\n  <span style=\"color: #228B22\">% check for color<\/span>\r\n  x2 = regexprep(x, <span style=\"color: #A020F0\">'(r|g|b|c|m|y|k|w)'<\/span>, <span style=\"color: #A020F0\">''<\/span>, <span style=\"color: #A020F0\">'once'<\/span>);\r\n\r\n  <span style=\"color: #228B22\">% check for marker<\/span>\r\n  x2 = regexprep(x2, <span style=\"color: #A020F0\">'(+|o|*|[^-]\\.|x|s|d|\\^|v|&gt;|&lt;|p|h)'<\/span>, <span style=\"color: #A020F0\">''<\/span>, <span style=\"color: #A020F0\">'once'<\/span>);\r\n\r\n  <span style=\"color: #228B22\">% check if the remainder is a valid line style (or empty)<\/span>\r\n  tf = ismember(x2, {<span style=\"color: #A020F0\">''<\/span>, <span style=\"color: #A020F0\">'-'<\/span>, <span style=\"color: #A020F0\">'--'<\/span>, <span style=\"color: #A020F0\">':'<\/span>, <span style=\"color: #A020F0\">'-.'<\/span>});\r\n<span style=\"color: #0000FF\">end<\/span>\r\n\r\n<span style=\"color: #0000FF\">if<\/span> tf\r\n  s = 1;\r\n\r\n<span style=\"color: #0000FF\">else<\/span>\r\n  <span style=\"color: #0000FF\">if<\/span> isequal(x, false) || strcmp(x, <span style=\"color: #A020F0\">'alpha'<\/span>)\r\n    <span style=\"color: #228B22\">% Next, check to see if it's \"alpha\"<\/span>\r\n    s = 2;\r\n\r\n  <span style=\"color: #0000FF\">elseif<\/span> isscalar(x) &amp;&amp; ishandle(x) &amp;&amp; strcmpi(get(x, <span style=\"color: #A020F0\">'Type'<\/span>), <span style=\"color: #A020F0\">'axes'<\/span>)\r\n    <span style=\"color: #228B22\">% Finally, check to see if it's a valid axes handle<\/span>\r\n    s = 3;\r\n\r\n  <span style=\"color: #0000FF\">else<\/span>\r\n    <span style=\"color: #228B22\">% Otherwise, return 0<\/span>\r\n    s = 0;\r\n\r\n  <span style=\"color: #0000FF\">end<\/span>\r\n<span style=\"color: #0000FF\">end<\/span><\/pre><h3>Comments<a name=\"16\"><\/a><\/h3>\r\n   <p>Let us know what you think <a href=\"https:\/\/blogs.mathworks.com\/pick\/?p=4389#respond\">here<\/a>.\r\n   <\/p><script language=\"JavaScript\">\r\n<!--\r\n\r\n    function grabCode_b659dd1ab2424ce3a54a4140b4a7c6fa() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='b659dd1ab2424ce3a54a4140b4a7c6fa ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' b659dd1ab2424ce3a54a4140b4a7c6fa';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        author = 'Jiro Doke';\r\n        copyright = 'Copyright 2013 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add author and copyright lines at the bottom if specified.\r\n        if ((author.length > 0) || (copyright.length > 0)) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (author.length > 0) {\r\n                d.writeln('% _' + author + '_');\r\n            }\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n      \r\n      d.title = title + ' (MATLAB code)';\r\n      d.close();\r\n      }   \r\n      \r\n-->\r\n<\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_b659dd1ab2424ce3a54a4140b4a7c6fa()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n            the MATLAB code \r\n            <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2012b<br><\/p>\r\n<\/div>\r\n<!--\r\nb659dd1ab2424ce3a54a4140b4a7c6fa ##### SOURCE BEGIN #####\r\n%%\r\n% This week,\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/15007\r\n% Jiro> is highlighting a code snippet submitted by Mikko Lepp\u00c3\u00a4nen.\r\n%\r\n% In my previous\r\n% <https:\/\/blogs.mathworks.com\/pick\/2013\/02\/01\/coding-challenge-on-input-parsing\/\r\n% post>, I posed a coding challenge to come up with an input validation\r\n% code snippet to work with\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/27485-boundedline-m |boundedline|>\r\n% by <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/287218\r\n% Kelly>. The function is a well-written piece of code with flexible input\r\n% arguments. Here are the different ways you could call the function:\r\n%\r\n%   [hl, hp] = boundedline(x, y, b)\r\n%   [hl, hp] = boundedline(x, y, b, linespec)\r\n%   [hl, hp] = boundedline(x1, y1, b1, linespec1, x2, y2, b2, linespec2)\r\n%   [hl, hp] = boundedline(. . ., 'alpha')\r\n%   [hl, hp] = boundedline(. . ., ax)\r\n%   [hl, hp] = boundedline(. . ., 'transparency', trans)\r\n%   [hl, hp] = boundedline(. . ., 'orientation', orient)\r\n%   [hl, hp] = boundedline(. . ., 'cmap', cmap)\r\n%\r\n% Here were the ground rules for the challenge:\r\n%\r\n% # The function should be able to handle all, except the 3rd, calling\r\n% syntax above. Any number of the optional arguments can be passed in.\r\n% # The input parameters should meet the respective criteria. (see help\r\n% boundedline)\r\n% # All optional inputs should have default values. (see help boundedline)\r\n% # You can assume that the parameter-value pair inputs will be called\r\n% after the other optional inputs.\r\n% # After parsing, the parameters should be put into a structure with\r\n% fields \"x\", \"y\", \"b\", \"linespec\", \"usealpha\", \"hax\", \"trans\", \"orient\",\r\n% \"cmap\".\r\n% # (Extra credit) Allow for the 3rd calling syntax.\r\n%\r\n% Mikko was the only person who rose to the challenge, and it was great!\r\n% His solution follows the ground rules, _and he even did the extra\r\n% credit!_ I wrote my own as well, but after seeing Mikko's solution, I was\r\n% able to improve mine. Today I am going to dissect both of our codes and\r\n% explain in detail.\r\n%\r\n%% Mikko's Solution\r\n% Rather than creating a single |inputParser|, Mikko makes use of\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/try.html |try\/catch|>\r\n% statements to deal with multiple scenarios. Let's take a look. _Note: I\r\n% made some minor modifications to his code for this post._\r\n%\r\n%   function res = validateInputs(varargin)\r\n%\r\n% *1.* First, an <https:\/\/www.mathworks.com\/help\/matlab\/ref\/inputparserclass.html\r\n% |inputParser|> object is created. Since there are 3 required inputs |x|,\r\n% |y|, and |b|, they are added to the |inputParser| object using the\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/inputparser.addrequired.html\r\n% |addRequired|> method. Note that\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/validateattributes.html\r\n% |validateattributes|> makes sure that the variables are numeric, finite,\r\n% and 2d.\r\n\r\nipObj1 = inputParser;\r\nipObj1.addRequired('x', @(x)validateattributes(x, {'numeric'}, {'finite', '2d'}));\r\nipObj1.addRequired('y', @(x)validateattributes(x, {'numeric'}, {'finite', '2d'}));\r\nipObj1.addRequired('b', @(x)validateattributes(x, {'numeric'}, {'finite'}));\r\n\r\n%%\r\n% *2.* Define optional parameters that could be passed.\r\n\r\nkeys = {'transparency', 'orientation', 'cmap'};\r\n\r\n%%\r\n% *3.* First try: consider a case where optional parameters |linespec|,\r\n% |useAlpha|, and |hax| have been passed. Note that the validation for\r\n% |useAlpha| and |hax| is checking for either the string \"alpha\" or a\r\n% handle. This allows the two optional values to be passed in any order.\r\n% See step 6, where it puts the parameters in the correct order. Following\r\n% the optional parameters, the three parameter-value pairs are defined.\r\n\r\ntry\r\n  ipObj2 = ipObj1.createCopy;\r\n  ipObj2.addOptional('linespec', '', @(x)ischar(x) && ~any(strcmp(x, ...\r\n                     [{'alpha'}, keys])));\r\n  ipObj2.addOptional('useAlpha', '', @(x)strcmp(x, 'alpha') || ishandle(x));\r\n  ipObj2.addOptional('hax', gca, @(x)strcmp(x, 'alpha') || ishandle(x));\r\n  ipObj2.addParamValue('orientation', 'horiz', ...\r\n                       @(x)any(validatestring(x, {'horiz', 'vert'})));\r\n  ipObj2.addParamValue('transparency', 0.2, @(x)validateattributes(x, ...\r\n                       {'numeric'}, {'scalar', 'positive'}));\r\n  ipObj2.addParamValue('cmap', [], @(x)validateattributes(x, {'numeric'}, ...\r\n                       {'2d', 'finite', 'size', [size(x, 1), 3]}));\r\n  \r\n  ipObj2.parse(varargin{:});\r\n  res = ipObj2.Results;\r\n  \r\n  %%\r\n  % *4.* If the above parsing fails, try a second parser. In this scenario,\r\n  % it is checking to see if the user supplied a second set of data for\r\n  % plotting - |x2|, |y2|, |b2|. _Note that this is the extra credit!_ \r\n  \r\ncatch me \r\n  try\r\n    ipObj3 = ipObj1.createCopy;\r\n    ipObj3.addOptional('linespec', '', @(x)ischar(x) && ~any(strcmp(x, ...\r\n                       [{'alpha'}, keys])));\r\n    ipObj3.addOptional('x2', [], @(x)validateattributes(x, {'numeric'}, ...\r\n                       {'finite', '2d'}));\r\n    ipObj3.addOptional('y2', [], @(x)validateattributes(x, {'numeric'}, ...\r\n                       {'finite', '2d'}));\r\n    ipObj3.addOptional('b2', [], @(x)validateattributes(x, {'numeric'}, ...\r\n                       {'finite'}));\r\n    ipObj3.addOptional('linespec2', '', @(x)ischar(x) && ~any(strcmp(x, ...\r\n                       [{'alpha'}, keys])));\r\n    ipObj3.addOptional('useAlpha', '', @(x)strcmp(x, 'alpha') || ishandle(x));\r\n    ipObj3.addOptional('hax', gca, @(x)strcmp(x, 'alpha') || ishandle(x));\r\n    ipObj3.addParamValue('orientation', 'horiz', @(x)any(validatestring(x, ...\r\n                         {'horiz', 'vert'})));\r\n    ipObj3.addParamValue('transparency', 0.2, @(x)validateattributes(x, ...\r\n                         {'numeric'}, {'scalar', 'positive'}));\r\n    ipObj3.addParamValue('cmap', [], @(x)validateattributes(x, {'numeric'}, ...\r\n                         {'2d', 'finite', 'size', [size(x, 1), 3]}));\r\n    \r\n    ipObj3.parse(varargin{:});\r\n    res = ipObj3.Results;\r\n    \r\n    %%\r\n    % *5.* If the above parsing fails, try a third parser. In this scenario, it\r\n    % is checking to see if the user omitted the |linespec| parameter.\r\n    \r\n  catch me2 \r\n    ipObj4 = ipObj1.createCopy;\r\n    ipObj4.addOptional('useAlpha', '', @(x)strcmp(x, 'alpha') || ishandle(x));\r\n    ipObj4.addOptional('hax', gca, @(x)strcmp(x, 'alpha') || ishandle(x));\r\n    ipObj4.addParamValue('orientation', 'horiz', @(x)any(validatestring(x, ...\r\n                         {'horiz', 'vert'})));\r\n    ipObj4.addParamValue('transparency', 0.2, @(x)validateattributes(x, ...\r\n                         {'numeric'}, {'scalar', 'positive'}));\r\n    ipObj4.addParamValue('cmap', [], @(x)validateattributes(x, {'numeric'}, ...\r\n                         {'2d', 'finite', 'size', [size(x, 1), 3]}));\r\n    ipObj4.addOptional('linespec', '', @(x)ischar(x) && ~any(strcmp(x, ...\r\n                       [{'alpha'}, keys])));\r\n\r\n    ipObj4.parse(varargin{:});\r\n    res = ipObj4.Results;\r\n  end\r\nend\r\n\r\n%%\r\n% *6.* If it passed all of the parsing above, |res| has the necessary\r\n% parameters. However, since |useAlpha| and |hax| could have been passed in\r\n% either order, Mikko checks to see if |useAlpha| is a handle or not. If it\r\n% is, he swaps |useAlpha| and |hax|.\r\n\r\nif ishandle(res.useAlpha)\r\n  tmp1 = res.useAlpha;\r\n  tmp2 = res.hax;\r\n  res.hax = tmp1;\r\n  res.useAlpha = tmp2;\r\nend\r\n\r\n%%\r\n% *7.* Finally, since |useAlpha| needs to be a logical (true or false), it\r\n% is set to true if it has the string \"alpha\". If not, it is set to false.\r\n\r\nif isequal(res.useAlpha, 'alpha')\r\n  res.useAlpha = true;\r\nelse\r\n  res.useAlpha = false;\r\nend\r\n\r\n%% My Solution\r\n% I also have a solution that I was able to improve based on a technique I\r\n% saw in Mikko's solution. I like the flexibility he introduced in the\r\n% optional arguments \"usealpha\" and \"hax\", where the user could provide\r\n% those inputs in either order. I've extended it to work for all 3 optional\r\n% arguments \"linespec\", \"usealpha\" and \"hax\". Here's what I have. For this\r\n% solution, I'm ignoring the \"extra credit\" part of the challenge.\r\n%\r\n% I have a custom validation function |validateOptionalArgs| for the 3\r\n% optional arguments \"linespec\", \"usealpha\", and \"hax\".\r\n\r\np = inputParser;\r\np.addRequired('x', @(x)validateattributes(x, {'numeric'}, {'finite', '2d'}));\r\np.addRequired('y', @(x)validateattributes(x, {'numeric'}, {'finite', '2d'}));\r\np.addRequired('b', @(x)validateattributes(x, {'numeric'}, {'finite'}));\r\np.addOptional('linespec', [], @(x) validateOptionalArgs(x) > 0);\r\np.addOptional('usealpha', [], @(x) validateOptionalArgs(x) > 0);\r\np.addOptional('hax'     , [], @(x) validateOptionalArgs(x) > 0);\r\np.addParamValue('orientation', 'horiz', @(x) ismember(x, {'vert', 'horiz'}));\r\np.addParamValue('transparency', 0.2, @(x) validateattributes(x, ...\r\n                {'numeric'}, {'scalar', '>=', 0, '<=', 1}));\r\np.addParamValue('cmap', colormap(gca), @(x) validateattributes(x, ...\r\n                {'double'}, {'size', [NaN, 3], '>=', 0, '<=', 1}));\r\n\r\np.parse(varargin{:});\r\nres = p.Results;\r\n\r\n%%\r\n% Since \"linespec\", \"usealpha\", and \"hax\" could be passed in any order, map\r\n% them back to the true parameters.\r\n\r\n% Default values\r\nlinespec = '';\r\nusealpha = false;\r\nhax = gca;\r\n\r\nfor iF = {'linespec', 'usealpha', 'hax'}\r\n  field = iF{1};\r\n  \r\n  switch validateOptionalArgs(res.(field))\r\n    case 1\r\n      linespec = res.(field);\r\n    case 2\r\n      usealpha = res.(field);\r\n    case 3\r\n      hax = res.(field);\r\n    case 0\r\n      % skip\r\n  end\r\nend\r\n\r\n% Map it back\r\n[res.linespec, res.usealpha, res.hax] = deal(linespec, usealpha, hax);\r\n\r\n%%\r\n% Finally, I set \"usealpha\" to TRUE if it has the string \"alpha\".\r\n\r\nif isequal(res.usealpha, 'alpha')\r\n  res.usealpha = true;\r\nend\r\n\r\n%%\r\n% Here's my |validateOptionalArgs| function which checks to see if the argument\r\n% matches one of the 3 optional inputs. It returns which option it matched.\r\n% If it did not match any, it returns 0.\r\n%\r\n%   function s = validateOptionalArgs(x)\r\n\r\n% s = validateOptionalArgs(x)\r\n%\r\n%   Returns 0, 1, 2, 3\r\n%       0 - did not match any\r\n%       1 - matched linespec\r\n%       2 - matched 'alpha'\r\n%       3 - matched hax\r\n\r\ntf = false;\r\n\r\n% First, check to see if it's a valid LineSpec\r\nif ischar(x)\r\n  % check for color\r\n  x2 = regexprep(x, '(r|g|b|c|m|y|k|w)', '', 'once');\r\n  \r\n  % check for marker\r\n  x2 = regexprep(x2, '(+|o|*|[^-]\\.|x|s|d|\\^|v|>|<|p|h)', '', 'once');\r\n  \r\n  % check if the remainder is a valid line style (or empty)\r\n  tf = ismember(x2, {'', '-', 'REPLACE_WITH_DASH_DASH', ':', '-.'});\r\nend\r\n\r\nif tf\r\n  s = 1;\r\n  \r\nelse\r\n  if isequal(x, false) || strcmp(x, 'alpha')\r\n    % Next, check to see if it's \"alpha\"\r\n    s = 2;\r\n    \r\n  elseif isscalar(x) && ishandle(x) && strcmpi(get(x, 'Type'), 'axes')\r\n    % Finally, check to see if it's a valid axes handle\r\n    s = 3;\r\n    \r\n  else\r\n    % Otherwise, return 0\r\n    s = 0;\r\n    \r\n  end\r\nend\r\n\r\n\r\n\r\n%% Comments\r\n%\r\n% Let us know what you think\r\n% <https:\/\/blogs.mathworks.com\/pick\/?p=4389#respond here>.\r\n\r\n##### SOURCE END ##### b659dd1ab2424ce3a54a4140b4a7c6fa\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   \r\n      This week, Jiro is highlighting a code snippet submitted by Mikko Lepp&auml;nen.\r\n      \r\n      In my previous post, I posed a coding challenge to come up with an input validation code... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/pick\/2013\/02\/22\/coding-challenge-on-input-parsing-result\/\">read more >><\/a><\/p>","protected":false},"author":35,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[16],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts\/4389"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/users\/35"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/comments?post=4389"}],"version-history":[{"count":8,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts\/4389\/revisions"}],"predecessor-version":[{"id":4398,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts\/4389\/revisions\/4398"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/media?parent=4389"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/categories?post=4389"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/tags?post=4389"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}