missing genvarname, with 'patch'

Robert Platt robert.platt at postgrad.manchester.ac.uk
Fri Mar 7 07:48:22 CST 2008


Hello,

It seems that genvarname, a function found in Matlab, is not available
in Octave yet. This function generates a valid, unique variable name
from a suggested name, as well as allowing name collisions to be avoided.

Here is an m file which provides an Octave version. It can be used in
the same way as the Matlab function, providing the same interface,
although I haven't tried to recreate the Matlab behaviour (Matlab's
genvarname creates very unreadable names. Also, we don't need to worry
about a name length limit in Octave).

Very sorry I didn't submit this as a patch: I didn't know where this
file would best be put (maybe in m/general). I will resubmit with
ChangeLog details etc if it makes things easier. Also this is my first
submission so I may do something stupid :-)

Best Regards
Rob

## Copyright (C) 2008 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
## <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn {Function File} {} genvarname (@var{ideal}, @var{avoid},
@var{allowTwoUnderscores})
## Returns a valid variable name in string format based upon @var{ideal}.
## If @var{ideal} is a cell array of strings, then a cell array of valid
## names base upon each string in @var{ideal} is returned instead.
##
## Optionally, names to be avoided by genvarname can be provided in
## @var{avoid} (which can be a string or cell array). genvarname will append
## a number to the generated variable name if it clashes with any name in
## @var{avoid}. This can be used with the command who() to prevent names
## that already exist in the the workspace from being returned. For example:
##
## @example
## x = 3.141;
## genvarname('x', who())
## @result{}
##           x1
## @end example
##
## Variable names beginning with two underscores '__' are valid but
## they are used internally by octave and should usually be avoided.
## genvarname therefore will not generate variable names beginning
## with two underscores. To turn off this behaviour, the optional
## argument @var{allowTwoUnderscores} can be set to true.
##
## For example, the default behaviour removes an underscore in the
## following case:
##
## @example
## genvarname('__x')
## @result{}
##          _x
## @end example
##
## However, the following code does not:
##
## @example
## genvarname('__x', '', true);
## @result{}
##          __x
## @end example
##
## Since variable names may only contain letters, digits and underscores,
## genvarname replaces any sequence of unallowed characters with
## an underscore. Also, variables may not begin with a digit; in this
## case an underscore is also added before the variable name.
##
## 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.
## @end deftypefn

function varname = genvarname(ideal, avoid, allowTwoUnderscores)

if nargin == 1  
    % turn avoid into an empty char array
    avoid = {};
    % default is to prevent two underscores at beginning of varname
    allowTwoUnderscores = false;
elseif nargin == 2 || nargin == 3
    % turn char array into 1x1 cell array of strings
    if ischar(avoid)
        avoid = {avoid};
    elseif ~iscellstr(avoid)
        error('genvarname: avoid must be a char array or cell array of
strings');
    end
   
    if nargin == 3
        if ~islogical(allowTwoUnderscores)
            error('genvarname: allowTwoUnderscores must be of type
logical');
        end
    else
        % default is to prevent two underscores at beginning of varname
        allowTwoUnderscores = false;
    end
else
    print_usage();
end

if ischar(ideal)
    varname = genvarname_onestring(ideal, avoid, allowTwoUnderscores);
elseif iscellstr(ideal)
    varname = cellfun(@genvarname_onhestring, avoid);
else
    error('genvarname: ideal must be a char array or cell array of
strings');
end

end

function varname = genvarname_onestring(ideal, avoid, allowTwoUnderscores)

% precondition: ideal is a char array and avoid is a cell array of strings
% this is checked in the calling genvarname routhine

varname = ideal;

% replace any non-word characters with an underscore
varname = regexprep(varname, '(\W+)', '_');

% Variable names can't begin with a number, so prepend underscore
% eg '12foo' would become '_12foo'
varname = regexprep(varname, '^(\d)', '_$1', 'once');

% In Octave, two underscores are usually reserved
% We can allow them with allowTwoUnderscores == true
if ~allowTwoUnderscores
    varname = regexprep(varname, '^_{2,}', '_', 'once');
end

if isempty(varname)
    varname = 'x';
end


% Append next available number to name to make sure varname is not
% in avoid and not a keyword

% start with varname, and the first number that would be appended is 1
testUnique = varname;
appendNo = 1;

% just to be extra nice, we append _1, _2, ... instead of 1, 2, 3
% if the variable ends with a number. This means that 'x2' would become
% 'x2_1' rather than 'x21' if it is found in the avoid cell array.
% This improves readablity.
if isempty(regexp(varname, '\d$', 'once'))
    % not ending in a digit, so no underscore.
    baseVarname = varname;
else
    % ends with a digit, append underscore.
    % (note: we never append this extra underscore if there is already
    % one at the end, so there's no danger of allowing two underscores
    % at the beginning of the name)
    baseVarname = strcat(varname, '_');
end

% this is the loop which appends the next available number
while any(strcmp(testUnique, avoid)) || iskeyword(testUnique)
    testUnique = strcat(baseVarname, num2str(appendNo));
    appendNo = appendNo + 1;
end
varname = testUnique;
   
end



More information about the Bug-octave mailing list