# HG changeset patch # User Rik # Date 1412105783 25200 # Node ID 5b64aee2257b88732553fd2bf842d97554db9cbc # Parent 8661727db27afaf1327eb2331f2a7664c4f711b4 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. diff -r 8661727db27a -r 5b64aee2257b scripts/miscellaneous/genvarname.m --- 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 ## Bill Denney -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 genvarname (char ("a", "b", "c")) +%!error genvarname (1) +%!error genvarname ("x", char ("a", "b", "c")) +%!error genvarname ("x", 1) +