classification
Title: itertools.count step
Type: enhancement Stage:
Components: Extension Modules Versions: Python 3.1, Python 2.7
process
Status: closed Resolution: accepted
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: LambertDW, mark.dickinson, rhettinger, steve21
Priority: low Keywords:

Created on 2009-01-22 13:14 by steve21, last changed 2009-02-12 18:47 by LambertDW. This issue is now closed.

Messages (13)
msg80368 - (view) Author: Steve (steve21) Date: 2009-01-22 13:14
I'd like to request a 'step' argument be added for itertools.count. It
would be useful in the same way that step is useful for the 'range'
function.

('range' and 'count' are very similar - 'count' could even be merged into
'range', for example by making 'range(None)' equivalent to 'count()')
msg80384 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2009-01-22 21:56
Am not too excited about this one.  As a heavy itertools user myself,
I've never had occasion to need a step argument.  As an implementer, I'm
aware that it would make the code for count() much more complex (because
of the internal optimizations and because step can be positive,
negative, or zero).

Have you had any real world use cases where count(start, step) would
have been the best solution?
msg80605 - (view) Author: Steve (steve21) Date: 2009-01-27 01:42
Here's a couple of functions I use with count and step:

def cf_e():
    '''return: (iterator) the infinite continued fraction for e
    e=[2; 1, 2, 1, 1, 4, 1, 1, 6, 1 , ... , 1, 2k, 1, ...]
    '''
    yield 2
    for k in itertools.count(2, 2):
        yield 1
        yield k
        yield 1


def prime_factors(n):
    '''n: (int > 1)
    return: (list) the sorted list of tuples (p,e) of prime factors of n
            p is a prime factor, e is the number of times the factor is used
    '''
    ret = []
    if n <= 1:
        return ret

    # factors: use known (small) primes, then possible primes (odd numbers)
    for factor in itertools.chain([2,3,5,7,11], itertools.count(13, 2)):
        if factor*factor > n:
            if n != 1:
                ret += [(n, 1)]
            break
        for e in itertools.count(0):
            div, mod = divmod(n, factor)
            if mod != 0:
                break
            n = div
        if e >= 1:
            ret += [(factor, e)]
    return ret
msg80609 - (view) Author: David W. Lambert (LambertDW) Date: 2009-01-27 02:48
Nice.  Now I know that $e$ is a least transcendental number.  But I 
can't figure out why inserting this code into your file (and removing 
some "itertools.") is difficult or unreadable.  I maintain a personal 
library of modules that I don't actually expect Guido to include in the 
python standard library.


import itertools

def count(offset=0,stride=1):
    for i in itertools.count():
        yield offset+i*stride


# this version probably performs faster
def count(offset=0,stride=1):
    while True:
        yield offset
        offset += stride
msg80614 - (view) Author: Steve (steve21) Date: 2009-01-27 03:56
I already use the second version of the count function you give (without
default arguments which I am not a big fan of). I'm not saying its
difficult or unreadable to bypass itertools.count and write your own
enhanced count function. But if I use a custom count function, you use a
custom count function, and possibly many others too, then there could be
a common requirement for a step argument and it might be a good idea too
make it more widely available. 

Haskell has a powerful and concise list notation, some of which Python
has already borrowed from:
multiple3 = [3,6..]
msg80618 - (view) Author: David W. Lambert (LambertDW) Date: 2009-01-27 04:37
Probably a better prime factor algorithm uses Sieve of E. to generate
primes through int(1+sqrt(n)) and test these.

The other algorithm uses a custom generator anyway.  Oh well, good luck,
I'll shut up.
You do have use cases that I couldn't think of.

Dave.
msg80643 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2009-01-27 10:55
I've looked again at what it would take to update the C code and think
that the optimizations there make it difficult to add this feature
without adding a lot of code complexity and without slowing-down the
common case.  Also, I value the simplicity of the current API.

Suggest you use "range(start, sys.maxsize, step)" or just roll your own
generator (like Lambert's second version).
msg81725 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2009-02-12 05:40
Found a way to do this while keeping full speed on the default case.

Also, fixed an error where the start argument was rounded to the nearest
integer.

Also, improved the utility over its cousin, range() by allowing floating
point arguments.

See r69522
msg81736 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-02-12 10:57
A couple of comments:

> Also, improved the utility over its cousin, range() by allowing floating
> point arguments.

Is this wise?  From a numerical perspective, it seems to me that using
count with floating-point arguments is almost always going to be the
wrong thing to do (unless those floats are integral), and that allowing
floating-point arguments invites misuse.  For example, I'd be suspicious
of code that looked like:

for x in count(0.0, 0.1):
    do_something_with_x

where it seems quite likely that what's intended is the more robust:

for i in count():
    x = i/10.0
    do_something_with_x


Second (unrelated) comment:  on my machine, list(count()) appears to
hang, and is unresponsive to keyboard interrupts.  Is there any easy way
to make this interruptible?  While list(count()) is clearly a stupid
thing to type, it would be nice if it didn't hang the interpreter.
msg81745 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2009-02-12 11:56
Not too worried about either issue.  For the first, it makes the code
closer to its pure python equivalent.  Better to add some math module
function like Matlab's linspace().  It's hard to prevent people from
creating numerical mistakes no matter what.  Besides, now it's possible
to write:  count(Decimal('1.1'), Decimal('.1')) and get exact
progressions.  

For the second, the non-interruptability issue is everpresent throughout
the language.  If we get some uniform way of dealing with it, that would
be great (though I expect it will slow down lots of things we care about
and provide nearly zero benefit).  Since count() and repeat() came out
in itertools eons ago, there have been zero bug reports or user
complaints about the issue.  So, I'll take it as a non-issue.
msg81748 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-02-12 12:02
> to write:  count(Decimal('1.1'), Decimal('.1')) and get exact
> progressions.  

That's pretty cool. :-)

> in itertools eons ago, there have been zero bug reports or user
> complaints about the issue.  So, I'll take it as a non-issue.

Fair enough.
msg81758 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2009-02-12 12:40
More coolness:

   count(Fraction(2,3), Fraction(1,6))
msg81800 - (view) Author: David W. Lambert (LambertDW) Date: 2009-02-12 18:47
I run my shells with low priority so I can sneak around and kill them.
History
Date User Action Args
2009-02-12 18:47:56LambertDWsetmessages: + msg81800
2009-02-12 12:40:33rhettingersetmessages: + msg81758
2009-02-12 12:02:18mark.dickinsonsetmessages: + msg81748
2009-02-12 11:56:49rhettingersetmessages: + msg81745
2009-02-12 10:57:48mark.dickinsonsetnosy: + mark.dickinson
messages: + msg81736
2009-02-12 05:42:46rhettingersetstatus: open -> closed
resolution: accepted
2009-02-12 05:40:41rhettingersetmessages: + msg81725
2009-01-27 10:55:49rhettingersetmessages: + msg80643
2009-01-27 04:37:53LambertDWsetmessages: + msg80618
2009-01-27 03:56:59steve21setmessages: + msg80614
2009-01-27 02:48:02LambertDWsetmessages: + msg80609
2009-01-27 01:42:17steve21setmessages: + msg80605
2009-01-23 04:36:58LambertDWsetnosy: + LambertDW
2009-01-22 21:56:03rhettingersetmessages: + msg80384
2009-01-22 18:43:00rhettingersetpriority: low
assignee: rhettinger
nosy: + rhettinger
components: + Extension Modules, - None
versions: + Python 3.1, Python 2.7, - Python 3.0
2009-01-22 13:14:30steve21create