# HG changeset patch # User bill@denney.ws # Date 1206644418 14400 # Node ID 76e7548add3f55c21c0c19211183346387678020 # Parent d448ac8a4874626adc655f26d03df5a01b222e0c genvarname.m: new function diff --git a/doc/interpreter/contributors.in b/doc/interpreter/contributors.in --- a/doc/interpreter/contributors.in +++ b/doc/interpreter/contributors.in @@ -146,6 +146,7 @@ Jim Peterson Danilo Piazzalunga Nicholas Piper +Robert Platt Hans Ekkehard Plesser Tom Poage Orion Poplawski diff --git a/scripts/ChangeLog b/scripts/ChangeLog --- a/scripts/ChangeLog +++ b/scripts/ChangeLog @@ -1,6 +1,10 @@ 2008-03-27 Bill Denney + * general/genvarname.m: New function. + * general/Makefile.in (SOURCES): Add it to the list. + * time/addtodate.m: New function. + * time/Makefile.in (SOURCES): Add it to the list. * geometry/rectint.m: Vectorize and add more tests. diff --git a/scripts/general/Makefile.in b/scripts/general/Makefile.in --- a/scripts/general/Makefile.in +++ b/scripts/general/Makefile.in @@ -33,10 +33,11 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ -SOURCES = __isequal__.m __splinen__.m accumarray.m arrayfun.m bicubic.m \ - bitcmp.m bitget.m bitset.m blkdiag.m cart2pol.m cart2sph.m cell2mat.m \ - celldisp.m circshift.m common_size.m cplxpair.m cumtrapz.m deal.m del2.m \ - diff.m flipdim.m fliplr.m flipud.m gradient.m idivide.m ind2sub.m int2str.m \ +SOURCES = __isequal__.m __splinen__.m accumarray.m arrayfun.m \ + bicubic.m bitcmp.m bitget.m bitset.m blkdiag.m cart2pol.m \ + cart2sph.m cell2mat.m celldisp.m circshift.m common_size.m \ + cplxpair.m cumtrapz.m deal.m del2.m diff.m flipdim.m fliplr.m \ + flipud.m genvarname.m gradient.m idivide.m ind2sub.m int2str.m \ interp1.m interp2.m interp3.m interpn.m interpft.m is_duplicate_entry.m \ isa.m isdefinite.m isdir.m isequal.m isequalwithequalnans.m \ isscalar.m issquare.m issymmetric.m isvector.m logical.m logspace.m \ diff --git a/scripts/general/genvarname.m b/scripts/general/genvarname.m new file mode 100644 --- /dev/null +++ b/scripts/general/genvarname.m @@ -0,0 +1,209 @@ +## Copyright (C) 2008 Bill Denney, Robert Platt +## +## This file is part of Octave. +## +## Octave is free software; you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or (at +## your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . + +## -*- texinfo -*- +## @deftypefn {Function File} {@var{varname} =} findfigs (@var{str}) +## @deftypefnx {Function File} {@var{varname} =} findfigs (@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). +## +## 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{} +## @{ +## [1,1] = foo +## [1,2] = foo1 +## @} +## @end group +## @end example +## +## Note that the result is a char array/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}. +## +## @example +## @group +## name = genvarname ("x"); +## eval([name " = 42"]); +## @result{} x = 42 +## @end group +## @end example +## +## Also, this can be useful for creating unique struct field names. +## +## @example +## @group +## x = struct (); +## for i = 1:3 +## x.(genvarname ("a", fieldnames (x))) = i; +## endfor +## @result{} +## x = +## @{ +## a = 1 +## a1 = 2 +## a2 = 3 +## @} +## @end group +## @end example +## +## Since variable names may only contain letters, digits and underscores, +## genvarname replaces any sequence of disallowed characters with +## an underscore. Also, variables may not begin with a digit; in this +## case an underscore is added before the variable name. +## +## Variable names beginning and ending with two underscores "__" are valid but +## they are used internally by octave and should generally be avoided, therefore +## genvarname will not generate such names. +## +## genvarname will also make sure that returned names do not clash with +## keywords such as "for" and "if". A number will be appended if necessary. +## Note, however, that this does @strong{not} include function names, +## such as "sin". Such names should be included in @var{avoid} if necessary. +## @seealso{isvarname, exist, tmpnam, eval} +## @end deftypefn + +## Authors: Rob Platt +## Bill Denney + +function varname = genvarname (str, exclusions) + + strinput = ischar (str); + ## Process the inputs + if (nargin < 2) + exclusions = {}; + elseif (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") + 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 = cstrcat ("A":"Z", "a":"z", "0":"9", "_"); + + varname = cell (size (str)); + for i = 1:numel (str) + ## Perform any modifications to the varname to make sure that it is + ## a valid variable name. + + ## remove invalid characters + str{i}(! ismember (str{i}, validchars)) = "_"; + ## do not use keywords + if (iskeyword (str{i})) + str{i} = cstrcat ("_", str{i}); + endif + ## double underscores at the beginning and end are reserved variables + underscores = (str{i} == "_"); + if (any (underscores)) + firstnon = find (!underscores, 1); + lastnon = find (!underscores, 1, "last"); + str{i}([1:firstnon-2, lastnon+2:end]) = []; + endif + ## The variable cannot be empty + if (isempty (str{i})) + str{i} = "x"; + endif + ## it cannot start with a number + if (ismember (str{i}(1), "0":"9")) + str{i} = cstrcat ("_", 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 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) = "_"; + endif + varname(i) = str(i); + idx = 0; + while excluded + idx++; + varname{i} = sprintf("%s%d", str{i}, idx); + excluded = any (strcmp (varname{i}, exclusions)); + endwhile + exclusions(end+1) = varname(i); + endfor + + if strinput + varname = varname{1}; + endif + +endfunction + +## Tests +## a single argument +%!assert(genvarname("a"), "a") +## a single argument with a non-conflicting exception +%!assert(genvarname("a", "b"), "a") +## a single argument with a conflicting exception +%!assert(genvarname("a", "a"), "a1") +## a single argument as a cell +%!assert(genvarname({"a"}), {"a"}) +%!assert(genvarname({"a"}, "b"), {"a"}) +%!assert(genvarname({"a"}, {"b"}), {"a"}) +%!assert(genvarname({"a"}, "a"), {"a1"}) +%!assert(genvarname({"a"}, {"a"}), {"a1"}) +## Test different arguments +## orientation +%!assert(genvarname({"a" "b"}), {"a" "b"}) +%!assert(genvarname({"a";"b"}), {"a";"b"}) +%!assert(genvarname({"a" "a"}), {"a" "a1"}) +%!assert(genvarname({"a" "b";"c" "d"}), {"a" "b";"c" "d"}) +%!assert(genvarname({"a" "a" "a";"a" "a" "a"}), {"a" "a2" "a4";"a1" "a3" "a5"}) +## more than one repetition +%!assert(genvarname({"a" "a" "a"}), {"a" "a1" "a2"}) +%!assert(genvarname({"a" "a" "a"}, {"a" "a1" "a2"}), {"a3" "a4" "a5"}) +## more than one repetition not in order +%!assert(genvarname({"a" "b" "a" "b" "a"}), {"a" "b" "a1" "b1" "a2"}) +## Variable name munging +%!assert (genvarname ("__x__"), "_x_") +%!assert (genvarname ("123456789"), "_123456789") +%!assert (genvarname ("_$1__"), "_1_") +%!assert (genvarname ("__foo__", "_foo_"), "_foo_1") +%!assert (genvarname ("1million_and1", "_1million_and1"), "_1million_and1_1") +%!assert (genvarname ({"", "", ""}), {"x", "x1", "x2"}) +%!assert (genvarname ("if"), "_if") +%!assert (genvarname ({"if", "if", "if"}), {"_if", "_if1", "_if2"})