Incrementing a string

Discussion in 'Python' started by John Velman, Sep 15, 2004.

  1. John Velman

    John Velman Guest

    I've used perl for a lot of 'throw away' scripts; I like Python better in
    principle, from reading about it, but it was always easier to just use
    perl rather than learn python.

    Now I'm writing a smallish program that I expect to keep around, so am
    taking this opportunity to try to learn some Python. I have a need for
    computer generated set of simple string lables. I don't know how many in
    advance---each is produced as a result of a user action.

    In perl I simply initiated

    $label= "a";

    Then, after using it doing

    $label++;

    This conveniently steps through the alphabet, then goes on to aa, ab,ac,
    ....

    In Python I can get from a to z with a generator as so:

    >>> def gen_alph():

    .... for i in range(97,123):
    .... yield chr(i)
    ....
    >>> g = gen_alph()
    >>> g.next()

    'a'
    >>> g.next()

    'b'
    >>> g.next()

    'c'

    But it looks like going beyond z to aa and so on is (relatively) complicated.

    In truth, it seems unlikely that I would ever go beyond z in using my
    application, and certainly not beyond zz which wouldn't be too hard to
    program. But I hate to build in limitations no matter how reasonable.

    It seems like there should be a better way that I'm missing because I'm
    thinking in perl, not thinking in Python. :)

    Best,

    John Velman
     
    John Velman, Sep 15, 2004
    #1
    1. Advertising

  2. John Velman

    Paul Rubin Guest

    John Velman <> writes:
    > But it looks like going beyond z to aa and so on is (relatively)
    > complicated.
    >
    > In truth, it seems unlikely that I would ever go beyond z in using my
    > application, and certainly not beyond zz which wouldn't be too hard to
    > program. But I hate to build in limitations no matter how reasonable.


    Python doesn't have that feature built in, so you have to think about
    how Perl really does it, and implement something similar in Python.
    Yes, it will be a little bit complicated, but the Perl implementers
    had to deal with the same complexity, and it's a decent exercise for
    you to do the same.
     
    Paul Rubin, Sep 16, 2004
    #2
    1. Advertising

  3. [John Velman]:
    >
    > [how do I implement "$a = 'z'; $a++;" in Python?]


    is there any reason you need your labels to be on this format?
    usually, I would find a plain "L42" more readable.

    def gen_label():
    num = 0
    while True:
    num += 1
    yield "L" + str(num)


    Perl's increment operator isn't a generator, it just changes the
    current value so you can jump back and forth. here's one quick go at
    it. I'm sure it can be done more prettily, and probably without
    assuming the string is ASCII :)

    def incr(s):
    i = 1
    while i <= len(s):
    if 'a' <= s[-i] < 'z':
    return s[:-i] + chr(88 + int(s[-i], 36)) + s[len(s)+1-i:]
    elif s[-i] == 'z':
    s = s[:-i] + 'a' + s[len(s)+1-i:]
    else:
    raise ValueError
    i += 1
    return 'a' + s

    --
    Kjetil T.
     
    Kjetil Torgrim Homme, Sep 16, 2004
    #3
  4. John Velman

    Phil Frost Guest

    #### begin sample program ####

    import string

    class LabelCounter(object):
    digits = string.lowercase

    def __init__(self, value=0):
    self.value = value

    def __str__(self):
    letters = []
    i = self.value
    while i:
    d, m = divmod(i, len(self.digits))
    letters.append(self.digits[m])
    i = d
    return ''.join(letters[::-1]) or self.digits[0]

    def __add__(self, other):
    return LabelCounter(self.value + other)

    def __lt__(self, other):
    return self.value < other

    # define other operators as needed; it's a shame this can't inherit from
    # int and get these for free. It can't do this because all of int's
    # operators return ints, not LabelCounters.


    i = LabelCounter(0)
    while i < 50:
    print i
    i += 1

    #### end sample program ####

    You can set 'digits' to any sequence at all. Set it to '01' to get
    output in binary, or to ['01','23','45','67','89'] to get base 5 in a
    very confusing notation *g*

    On Wed, Sep 15, 2004 at 03:08:20PM -0700, John Velman wrote:
    > I've used perl for a lot of 'throw away' scripts; I like Python better in
    > principle, from reading about it, but it was always easier to just use
    > perl rather than learn python.
    >
    > Now I'm writing a smallish program that I expect to keep around, so am
    > taking this opportunity to try to learn some Python. I have a need for
    > computer generated set of simple string lables. I don't know how many in
    > advance---each is produced as a result of a user action.
    >
    > In perl I simply initiated
    >
    > $label= "a";
    >
    > Then, after using it doing
    >
    > $label++;
    >
    > This conveniently steps through the alphabet, then goes on to aa, ab,ac,
    > ...
    >
    > In Python I can get from a to z with a generator as so:
    >
    > >>> def gen_alph():

    > ... for i in range(97,123):
    > ... yield chr(i)
    > ...
    > >>> g = gen_alph()
    > >>> g.next()

    > 'a'
    > >>> g.next()

    > 'b'
    > >>> g.next()

    > 'c'
    >
    > But it looks like going beyond z to aa and so on is (relatively) complicated.
    >
    > In truth, it seems unlikely that I would ever go beyond z in using my
    > application, and certainly not beyond zz which wouldn't be too hard to
    > program. But I hate to build in limitations no matter how reasonable.
    >
    > It seems like there should be a better way that I'm missing because I'm
    > thinking in perl, not thinking in Python. :)
    >
    > Best,
    >
    > John Velman
    > --
    > http://mail.python.org/mailman/listinfo/python-list
     
    Phil Frost, Sep 16, 2004
    #4
  5. John Velman wrote:

    > $label++;
    >
    > This conveniently steps through the alphabet, then goes on to aa, ab,ac,
    > ...
    >
    > In Python I can get from a to z with a generator as so:

    [snip]
    > But it looks like going beyond z to aa and so on is (relatively)
    > complicated.


    Here's one way, perhaps it is simple enough for you:

    import string

    def strpp(s):
    for i in s:
    yield i
    for i in strpp(s):
    for j in s:
    yield i + j

    labels = strpp(string.lowercase)
    label = label.next() # ad infinitum


    Hope you enjoy Python,
    Jeffrey
     
    Jeffrey Froman, Sep 16, 2004
    #5
  6. Re: Incrementing a string -- puzzing "solution"...

    On Wed, 15 Sep 2004 15:08:20 -0700 John wrote:

    > This conveniently steps through the alphabet, then goes on to aa, ab,ac,


    Puzzling... I hacked at this problem as a flexibility learning exercise
    and actually got this to work; but... does 'while True' in a generator
    have side-effects...?? Or generator recursion?

    Maybe I'm just tired -- I don't see how the string actually _grows_ inside
    the while.... :^?

    I was just expecting the prefix to be '', then 'a'...'z' giving a..z,
    aa..az, ba..bz, ... za..zz -- not continuing through aaa...azz and
    onwards... It's cool; but boggles my mind at the moment... not that
    that's a stretch... :^)

    def ascinc(start='a',end='z'):
    g = ascinc(start,end)
    prefix = ''
    while True:
    for i in range(ord(start[-1]),ord(end[-1])+1):
    yield (prefix + chr(i))
    prefix = g.next()


    print "what you wanted..."
    g = ascinc('a')

    for i in range(100):
    print g.next(),

    print
    print "including some flexibility..."

    g = ascinc('0','1')

    for i in range(100):
    print g.next(),
     
    Pierre Fortin, Sep 16, 2004
    #6
  7. John Velman

    Paul Rubin Guest

    Here's my generator version:

    def gen_alph():
    def convert(k,n):
    # convert integer k to n-letter string
    out = []
    for i in xrange(n):
    k,r = divmod(k,26)
    out.append(chr(97+r))
    out.reverse()
    return ''.join(out)

    nletters = 1
    while True:
    for k in xrange(26**nletters):
    yield convert(k, nletters)
    nletters += 1

    To test it:

    g = gen_alph()
    for i in range(40):
    print i, g.next()
     
    Paul Rubin, Sep 16, 2004
    #7
  8. John Velman

    Andrew Dalke Guest

    Phil Frost wrote:
    > import string
    >
    > class LabelCounter(object):
    > digits = string.lowercase

    ....
    > # define other operators as needed; it's a shame this can't inherit from
    > # int and get these for free. It can't do this because all of int's
    > # operators return ints, not LabelCounters.


    Try this instead. As usual with clever solutions, you
    probably shouldn't use this in real code.

    import string

    def fixup(klass):
    for name, meth in inspect.getmembers(klass):
    # Find all the methods except for a few special ones
    # we know won't return integers
    if (callable(meth) and name not in
    ["__class__", "__new__", "__init__", "__str__", "__repr__"]):

    # Need to make a wrapper function (g) for each of these methods.
    # The wrapper function needs to know the original method, which
    # is stored in the newly created scope (f).
    def f(meth = meth):
    def g(self, *args, **kwargs):
    retval = meth(self, *args, **kwargs)
    if isinstance(retval, int):
    return LabelCounter(retval)
    return retval
    return g
    g = f()
    setattr(klass, name, g) # replace with the wrapped version

    class LabelCounter(int):
    digits = string.ascii_lowercase
    def __str__(self):
    letters = []
    i = int(self)
    while i:
    d, m = divmod(i, len(self.digits))
    letters.append(self.digits[m])
    i = d
    return "".join(letters[::-1]) or self.digits[0]
    __repr__ = __str__


    Here it is in use.

    >>> a = LabelCounter(4)
    >>> print a

    e
    >>> print a+1

    f
    >>> print a+10

    o
    >>> print (a+10)/2

    h
    >>> print a

    e
    >>> print a/2

    c
    >>> print a/3

    b
    >>> print a/30

    a
    >>> print a**4

    jw
    >>>


    > You can set 'digits' to any sequence at all. Set it to '01' to get
    > output in binary, or to ['01','23','45','67','89'] to get base 5 in a
    > very confusing notation *g*


    I set it to ascii_lowercase since string.lowercase is locale
    dependent.

    >>> import string, locale
    >>> string.lowercase

    'abcdefghijklmnopqrstuvwxyz'
    >>> locale.setlocale(locale.LC_ALL, "de_DE")

    'de_DE'
    >>> string.lowercase

    'abcdefghijklmnopqrstuvwxyz\xaa\xb5\xba\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
    >>>


    OTOH, that might be what you wanted ...

    Andrew
     
    Andrew Dalke, Sep 16, 2004
    #8
  9. John Velman

    Duncan Booth Guest

    Jeffrey Froman wrote:

    > import string
    >
    > def strpp(s):
    > for i in s:
    > yield i
    > for i in strpp(s):
    > for j in s:
    > yield i + j
    >
    > labels = strpp(string.lowercase)
    > label = label.next() # ad infinitum
    >
    >


    string.ascii_lowercase may be preferable here.
     
    Duncan Booth, Sep 16, 2004
    #9
  10. John Velman

    John Velman Guest

    Thanks to everyone who responded! I've learned a good deal by reading
    through the solutions. Don't know which one I'll use yet (or perhaps I'll
    use something suggested by them), but these take me another step toward
    'thinking in Python'. Also, I might add, it's really hard to know of all
    the machinery that is available --- there is so much of it. I now know
    about the itertools module, for example.

    Kjetil Torgrim Homme asked why not just make the labels L1, L2, ..., Paul
    Foley proposed similarly: label1, label2, ..., and also suggested that if
    $label="one" it would make sense for $label++ to be "two".

    Either of these would work (although "one","two",... would be yet a
    different programming problem). I prefer my initial choice because the
    labels will be used interactively, and I prefer to be able to do
    everything from the keyboard. It's easier for me to type "aq" than "L42".
    I'm a pretty good touch typist as far as lower case letters go, but
    usually have to look at the keyboard to type numerals. So "m" is
    preferable to "L12" but, to me, there isn't much difference between
    typing "l12" and "label12".

    I think some of the proposed solutions would make it easy to taylor the
    kind of label as a user preference if I ever get this application to the
    point that I think someone else might be interested in using it.

    And yes, I think I'm going to enjoy Python.

    Best,

    John Velman
     
    John Velman, Sep 16, 2004
    #10
  11. John Velman

    Terry Reedy Guest

    Re: Incrementing a string -- puzzing "solution"...

    "Pierre Fortin" <> wrote in message
    news:...
    > On Wed, 15 Sep 2004 15:08:20 -0700 John wrote:
    > Puzzling... I hacked at this problem as a flexibility learning exercise
    > and actually got this to work; but... does 'while True' in a generator
    > have side-effects...??


    No. Note that the 'while True' and the rest of the body code end up in/as
    the body of the .next method of the generator object produced by calling
    the generator function. You don't have to understand all this to use
    generators in for statements, but you should when using them with while
    loops.

    > Or generator recursion?


    I has a main of sometimes simplifying variable depth recursion ;-)

    > I don't see how the string actually _grows_ inside the while.... :^?
    > I was just expecting the prefix to be '', then 'a'...'z' giving a..z,
    > aa..az, ba..bz, ... za..zz -- not continuing through aaa...azz and
    > onwards... It's cool; but boggles my mind at the moment... not that
    > that's a stretch... :^)


    The following is an never-ending generator, with unbounded levels of
    recursion. The generator on each level of recursion is the same and
    therefore generates the same sequence. Each is 'behind' the level above it
    and 'ahead' of the level beneath (counting the first as the top level) in
    terms of where it is in the sequence.

    Prefix grows from length 0 to 1 on the first call to the next method of the
    level beneath it. That same call causes the creation of another 'instance'
    (still inactive) two levels down. When a later g.next call causes the
    prefix one level down to grow from 0 to 1, the prefix as the current level
    grows from 1 to 2. And so on. At any time (except transitions), there is
    a stack of n+1 identical instances of the generator (and its .next methods)
    with prefixes ranging from length n-1 down to 0 with the bottom instance
    having no prefix at all.

    > def ascinc(start='a',end='z'):
    > g = ascinc(start,end)


    This creates a new generator object but does NOT call its .next method.

    > prefix = ''
    > while True:
    > for i in range(ord(start[-1]),ord(end[-1])+1):
    > yield (prefix + chr(i))
    > prefix = g.next()


    Increments in prefix length bubble up from the bott

    > g = ascinc('a')


    This 'kickstarts' the process by creating the top generator without calling
    it.

    > for i in range(100):
    > print g.next(),


    while True: print g.next() # would theorectically continue indefinitely, as
    would
    for s in ascinc('a'): print s # both would croak on the recursion limit

    In each case, the first call of .next() causes creation of a second
    generator and a blank prefix. I hope the mechanism is now clearer for you.

    Terry J. Reedy
     
    Terry Reedy, Sep 17, 2004
    #11
  12. Re: Incrementing a string -- puzzing "solution"...

    On Thu, 16 Sep 2004 21:13:50 -0400 Terry wrote:

    > I hope the mechanism is now clearer for you.


    Yes, thank you... I was getting pretty sleepy at the time and I thought
    this was likely -- obviously since I coded it... :^) I was wanting the
    effect I finally got, just was unclear how it was actually occurring....

    One more notch on the road to expert level... :>

    -30-

    BTW, someone wondered about "-30-"...

    From journalism handbooks:
    At the end of a story, write "End" or "30" -- a journalism symbol
    for "finish".

    I use it when I'm pretty sure I'm done with a thread...

    Regards,
    Pierre
     
    Pierre Fortin, Sep 17, 2004
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Josh Graham
    Replies:
    3
    Views:
    23,294
    Nicolas Matringe
    Apr 7, 2004
  2. Salman

    Incrementing value test

    Salman, Apr 13, 2005, in forum: VHDL
    Replies:
    2
    Views:
    642
    Engineering Guy
    Apr 13, 2005
  3. Mothra
    Replies:
    3
    Views:
    438
  4. Ron
    Replies:
    0
    Views:
    316
  5. Matthias Güntert
    Replies:
    1
    Views:
    382
    Simon Forman
    Aug 20, 2009
Loading...

Share This Page