{"id":238,"date":"2016-08-10T10:53:49","date_gmt":"2016-08-10T18:53:49","guid":{"rendered":"http:\/\/wonghoi.humgar.com\/blog\/?p=238"},"modified":"2025-04-19T23:39:25","modified_gmt":"2025-04-20T07:39:25","slug":"matlab-techniques-self-identifying-by-type-methods","status":"publish","type":"post","link":"https:\/\/wonghoi.humgar.com\/blog\/2016\/08\/10\/matlab-techniques-self-identifying-by-type-methods\/","title":{"rendered":"MATLAB Techniques: Self-identifying (by type) methods"},"content":{"rendered":"<p>We all know MATLAB by default fill numbers with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">0<\/code> if we haven&#8217;t specified them (such as expanding a matrix by\u00a0injecting values beyond\u00a0the original matrix size). Cells are default filled with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">{[]}<\/code> even if you meant\u00a0to have <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">cellstr()<\/code>\u00a0 <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\"> {''}<\/code> across the board. Sometimes it&#8217;s not what we wanted. <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">0<\/code> can be legitimate value, so we want <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">NaN<\/code> to denote\u00a0undefined values. Same as <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">cellstr()<\/code>, we don&#8217;t want to choke the default string processing programs\u00a0because one stupid <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">{[]}<\/code> turns the entire cell array into\u00a0to a non-cellstr array.<\/p>\n<p>For compatibility reasons (and it&#8217;s also hard to do so), it&#8217;s not a good idea to simply modify the factory behavior. I have something similar to table() objects that requires similar expansion on\u00a0arbitrary data types, but MATLAB&#8217;s defaults proves to be clumsy.<\/p>\n<p>Instead of writing <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">switch-case<\/code>\u00a0statements or a bunch of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">if<\/code>\u00a0statements that relies on type information like this:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">function x = makeUndefined(x)\n  switch class(x)\n    case {'double', 'single'}\n      x = NaN(size(x));\n    case 'cell'\n      if( iscellstr(x) )\n        x = repmat({''}, size(x));\n      end\n    % ...\n  end\n<\/pre>\n<p>I found a slick way to do it so I don&#8217;t have to keep track of it again if I need the same defaults in other places: take advantage of the fact that MATLAB selectively will load\u00a0the right method depending on the first input argument(s)*:<\/p>\n<p>Create\u00a0a commonly named method (e.g. <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">makeUndefined()<\/code>) for the PODs and put it under the POD&#8217;s @folder (e.g. <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">\/@double\/makeUndefined.m<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">\/@cell\/makeUndefined.m<\/code>). The functions look something like this:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">function y = makeUndefined(x)\n% This function must be put under \/@double\n  y = NaN(size(x));<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">function x = makeUndefined(x)\n% This function must be put under \/@cell\n  if( iscellstr(x) )\n    x = repmat({''}, size(x));\n  end<\/pre>\n<p>Similarly, you can make your <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">isundefined()<\/code> implementation for <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">@double<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">@cell,<\/code> etc, just like the factory-shipped\u00a0<code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">@categorical\/isundefined()<\/code> corresponding to the same rules you set for <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">makeUndefined()<\/code>.<\/p>\n<p>Actually, the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">switch-case<\/code> approach is analogous to the common abuses of RTTI in C++: every time a new type is added, you have to open all the methods that depends on the type info and update them, instead of having the classes implement those methods (with proper object hierarchy and overloading).<\/p>\n<blockquote><p><em>[Scott Meyers, &#8220;Effective C++&#8221;] Anytime you find yourself writing code of the form<\/em>\u00a0&#8220;<em>if the object is of type T1, then do something, but if it&#8217;s of type T2, then do something else<\/em>,&#8221;\u00a0<em>slap yourself<\/em><\/p><\/blockquote>\n<hr \/>\n<p>This technique is especially valuable when you and TMW (or other users) have different ideas of what an English word (e.g. <em>empty<\/em>, <em>defined<\/em>, <em>numeric<\/em>) means. Like do you consider boolean (logical) <em>numeric<\/em>? TMW says no through <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">isnumeric()<\/code>.<\/p>\n<p>To give you an example, I made a tool to nicely plot arbitrary features in my <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">@table<\/code> over time (the equivalent of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">@timetable<\/code> before TMW introduced it). It only make sense if the associated dependent variable (y-axis) can be quantified, so\u00a0<strong>what I meant is broader<\/strong> than <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">isnumeric()<\/code>: it&#8217;s <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">isConvertibleToDouble()<\/code> since I casted my dependent variables with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">double()<\/code> in between.<\/p>\n<p>Boolean (logical) and categorical variables have quantifiable levels, so <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">double()<\/code> can be applied to them, they should return <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">TRUE<\/code> for <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">isConvertibleToDouble()<\/code> despite <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">isnumeric()<\/code> returns <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">FALSE<\/code>. They have the same behavior for basic types like <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">double(), single(), char(), cellstr(), struct()<\/code>, etc.<\/p>\n<p>In summary,<\/p>\n<ol>\n<li><span style=\"text-decoration: underline;\">You say what you really mean<\/span> (by introducing nomenclature), NOT what it <strong>typically<\/strong> does<br \/>\n&#8211; this is like creating another indirection like <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">half(x)<\/code> instead of directly writing <code class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">x\/2<\/code> or <code class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">x&gt;&gt;1<\/code>.<br \/>\n&#8211; spend 90% of your time coming up with a very intuitive yet precise name. &#8216;Misspoke&#8217; == Bug!<\/li>\n<li>The new <span style=\"text-decoration: underline;\">data types self-manage through implementing methods<\/span> used by your code.<br \/>\n&#8211; assume nothing about input type other than the interfaces that are accessed through<br \/>\n(the traditional approach knows exactly what inputs they&#8217;re going to see)<br \/>\n&#8211; if you did #1 correctly, there&#8217;s no reason to foresee\/prepare-for new input types<br \/>\n(just implement the methods for the input data types that you want it to run for now)<br \/>\n&#8211;\u00a0no sweep (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">switch-otherwise<\/code>) case to mishandle** unexpected new input data types<br \/>\n(because it won&#8217;t run on an input data type until all called methods are implemented)<br \/>\n&#8211; introducing new input data types won&#8217;t break the core code for existing types.<br \/>\n(new input data types can only break themselves if they implemented the methods wrong)<\/li>\n<\/ol>\n<hr \/>\n<p>* This is tricky business. MATLAB doesn&#8217;t have function overloading (by signature), but will look into the type\/class of FIRST dominant input argument and load the appropriate classes. Usually it&#8217;s the first argument, but for non-POD classes, you can specify the dominance relationship (<a href=\"http:\/\/www.mathworks.com\/help\/matlab\/matlab_oop\/class-precedence.html\">Inferior classes<\/a>). Actually little has been said about such relationship in PODs in the official documentation.<\/p>\n<p>I called support and found that there&#8217;s no dominance relationship in PODs, so it&#8217;s pretty much the first argument. That means this trick does not work if you want to overload\u00a0<code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">bsxfun()<\/code> for say, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">nominal()<\/code> objects (which doesn&#8217;t have a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">bsxfun()<\/code> implementation) keeping the same input argument order because the first argument is a function handle\u00a0for both the factory and the user method. Bummer!<\/p>\n<p>[EDIT 2025\/04\/16]: Found new official <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/matlab_oop\/matlab-vs-other-oo-languages.html\">documentation about the design consideration of MATLAB&#8217;s OOP<\/a> explicitly saying it&#8217;s the first (leftmost) argument that decides which function that shares the same function name gets called:<\/p>\n<p><a href=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2016\/08\/2025-04-16_15-06.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6327\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2016\/08\/2025-04-16_15-06.png\" alt=\"\" width=\"1361\" height=\"213\" srcset=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2016\/08\/2025-04-16_15-06.png 1361w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2016\/08\/2025-04-16_15-06-300x47.png 300w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2016\/08\/2025-04-16_15-06-1024x160.png 1024w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2016\/08\/2025-04-16_15-06-768x120.png 768w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2016\/08\/2025-04-16_15-06-500x78.png 500w\" sizes=\"auto, (max-width: 1361px) 100vw, 1361px\" \/><\/a><\/p>\n<p>This is why the new &#8216;<code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">*_fun<\/code>&#8216; functions I write, I always use the object to operate on as the first argument whenever possible. Gets a little bit ugly when I want to support multiple inputs like <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">cellfun()<\/code>, so I have to weight whether it&#8217;s worth the confusion for the overloading capability.<\/p>\n<p>** Unless you want to torture yourself by listing all recognized types and make sure the &#8216;<code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">switch-otherwise<\/code>&#8216; case fails.<\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_238\" class=\"pvc_stats all  \" data-element-id=\"238\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"excerpt":{"rendered":"<p>We all know MATLAB by default fill numbers with 0 if we haven&#8217;t specified them (such as expanding a matrix by\u00a0injecting values beyond\u00a0the original matrix size). Cells are default filled with {[]} even if you meant\u00a0to have cellstr()\u00a0 {&#8221;} across &hellip; <a href=\"https:\/\/wonghoi.humgar.com\/blog\/2016\/08\/10\/matlab-techniques-self-identifying-by-type-methods\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_238\" class=\"pvc_stats all  \" data-element-id=\"238\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[25,10,6],"tags":[26],"class_list":["post-238","post","type-post","status-publish","format-standard","hentry","category-cpp","category-matlab","category-note-to-self","tag-rtti"],"_links":{"self":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/238","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/comments?post=238"}],"version-history":[{"count":20,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/238\/revisions"}],"predecessor-version":[{"id":6393,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/238\/revisions\/6393"}],"wp:attachment":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/media?parent=238"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/categories?post=238"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/tags?post=238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}