changeset 19228:5b64aee2257b

genvarname.m: Overhaul function. 25% speed increase by replacing ismember with dedicated functions. * genvarname.m: Redo docstring. Use default values for EXCLUSIONS input argument to simplify parsing. Validate number of inputs is correct. Fix bug if EXCLUSIONS input is a matrix rather than a vector. Replace ismember with isalnum or isdigit to speed up function. Add BIST tests for input validation.
author Rik <rik@octave.org>
date Tue, 30 Sep 2014 12:36:23 -0700
parents 8661727db27a
children 23519ad614da
files scripts/miscellaneous/genvarname.m
diffstat 1 files changed, 56 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/miscellaneous/genvarname.m	Tue Sep 30 11:45:48 2014 -0700
+++ b/scripts/miscellaneous/genvarname.m	Tue Sep 30 12:36:23 2014 -0700
@@ -19,26 +19,13 @@
 ## -*- texinfo -*-
 ## @deftypefn  {Function File} {@var{varname} =} genvarname (@var{str})
 ## @deftypefnx {Function File} {@var{varname} =} genvarname (@var{str}, @var{exclusions})
-## Create unique variable(s) from @var{str}.  If @var{exclusions} is
-## given, then the variable(s) will be unique to each other and to
-## @var{exclusions} (@var{exclusions} may be either a string or a cellstr).
+## Create valid unique variable name(s) from @var{str}.
 ##
 ## If @var{str} is a cellstr, then a unique variable is created for each
 ## cell in @var{str}.
 ##
 ## @example
 ## @group
-## x = 3.141;
-## genvarname ("x", who ())
-##   @result{} x1
-## @end group
-## @end example
-##
-## If @var{wanted} is a cell array, genvarname will make sure the returned
-## strings are distinct:
-##
-## @example
-## @group
 ## genvarname (@{"foo", "foo"@})
 ##   @result{}
 ##      @{
@@ -48,7 +35,19 @@
 ## @end group
 ## @end example
 ##
-## Note that the result is a char array/cell array of strings, not the
+## If @var{exclusions} is given, then the variable(s) will be unique to each
+## other and to @var{exclusions} (@var{exclusions} may be either a string or
+## a cellstr).
+##
+## @example
+## @group
+## x = 3.141;
+## genvarname ("x", who ())
+##   @result{} x1
+## @end group
+## @end example
+##
+## Note that the result is a char array or cell array of strings, not the
 ## variables themselves.  To define a variable, @code{eval()} can be
 ## used.  The following trivial example sets @code{x} to @code{42}.
 ##
@@ -60,7 +59,7 @@
 ## @end group
 ## @end example
 ##
-## Also, this can be useful for creating unique struct field names.
+## This can be useful for creating unique struct field names.
 ##
 ## @example
 ## @group
@@ -77,50 +76,53 @@
 ## @end group
 ## @end example
 ##
-## Since variable names may only contain letters, digits and underscores,
-## genvarname replaces any sequence of disallowed characters with
+## Since variable names may only contain letters, digits, and underscores,
+## @code{genvarname} will replace any sequence of disallowed characters with
 ## an underscore.  Also, variables may not begin with a digit; in this
-## case an x is added before the variable name.
+## case an @samp{x} is added before the variable name.
 ##
 ## Variable names beginning and ending with two underscores @qcode{"__"} are
-## valid but they are used internally by octave and should generally be
-## avoided, therefore genvarname will not generate such names.
+## valid, but they are used internally by Octave and should generally be
+## avoided; therefore, @code{genvarname} will not generate such names.
 ##
-## genvarname will also make sure that returned names do not clash with
+## @code{genvarname} will also ensure that returned names do not clash with
 ## keywords such as @qcode{"for"} and @qcode{"if"}.  A number will be
 ## appended if necessary.  Note, however, that this does @strong{not} include
-## function names, such as @qcode{"sin"}.  Such names should be included in
-## @var{avoid} if necessary.
-## @seealso{isvarname, exist, tmpnam, eval}
+## function names such as @qcode{"sin"}.  Such names should be included in
+## @var{exclusions} if necessary.
+## @seealso{isvarname, iskeyword, exist, who, tmpnam, eval}
 ## @end deftypefn
 
 ## Authors: Rob Platt <robert.platt@postgrad.manchester.ac.uk>
 ##          Bill Denney <bill@denney.ws>
 
-function varname = genvarname (str, exclusions)
+function varname = genvarname (str, exclusions = {})
+
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  endif
 
   strinput = ischar (str);
   ## Process the inputs
-  if (nargin < 2)
-    exclusions = {};
-  elseif (ischar (exclusions))
+  if (strinput)
+    if (rows (str) != 1)
+      error ("genvarname: if more than one STR is given, it must be a cellstr");
+    endif
+    str = {str};
+  elseif (! iscellstr (str))
+    error ("genvarname: STR must be a string or cellstr");
+  endif
+
+  if (ischar (exclusions))
     if (rows (exclusions) != 1)
       error ("genvarname: if more than one exclusion is given, it must be a cellstr");
     endif
     exclusions = {exclusions};
   elseif (! iscellstr (exclusions))
-    error ("genvarname: EXCLUSIONS must be a string or a cellstr");
+    error ("genvarname: EXCLUSIONS must be a string or cellstr");
+  else
+    exclusions = exclusions(:);
   endif
-  if (ischar (str))
-    if (rows (str) != 1)
-      error ("genvarname: if more than one STR is given, it must be a cellstr");
-    endif
-    str = {str};
-  elseif (! iscellstr (str))
-    error ("genvarname: STR must be a string or a cellstr");
-  endif
-
-  validchars = ["A":"Z", "a":"z", "0":"9", "_"];
 
   varname = cell (size (str));
   for i = 1:numel (str)
@@ -128,7 +130,7 @@
     ## a valid variable name.
 
     ## remove invalid characters
-    str{i}(! ismember (str{i}, validchars)) = "_";
+    str{i}(! (isalnum (str{i}) | str{i} == "_")) = "_";
     ## do not use keywords
     if (iskeyword (str{i}))
       firstcharacter = toupper (str{i}(1));
@@ -143,17 +145,17 @@
       str{i} = ["x", str{i}];
     endif
     ## it cannot start with a number
-    if (ismember (str{i}(1), "0":"9"))
+    if (isdigit (str{i}(1)))
       str{i} = ["x", str{i}];
     endif
 
     ## make sure that the variable is unique relative to other variables
     ## and the exclusions list
     excluded = any (strcmp (str{i}, exclusions));
-    if (excluded && ismember (str{i}(end), "0":"9"))
+    if (excluded && isdigit (str{i}(end)))
       ## if it is not unique and ends with a digit, add an underscore to
       ## make the variable name more readable ("x1_1" instead of "x11")
-      str{i}(end+1) = "_";
+      str{i} = [str{i}, "_"];
     endif
     varname(i) = str(i);
     idx = 0;
@@ -205,4 +207,14 @@
 %!assert (genvarname ({"", "", ""}), {"x", "x1", "x2"})
 %!assert (genvarname ("if"), "xIf")
 %!assert (genvarname ({"if", "if", "if"}), {"xIf", "xIf1", "xIf2"})
+## Exclusions in odd format
+%!assert (genvarname ("x", {"a", "b"; "x", "d"}), "x1")
 
+%% Test input validation
+%!error genvarname ()
+%!error genvarname (1,2,3)
+%!error <more than one STR is given, it must be a cellstr> genvarname (char ("a", "b", "c"))
+%!error <STR must be a string or cellstr> genvarname (1)
+%!error <more than one exclusion is given, it must be a cellstr> genvarname ("x", char ("a", "b", "c"))
+%!error <EXCLUSIONS must be a string or cellstr> genvarname ("x", 1)
+