Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

itertools.count step #49282

Closed
steve21 mannequin opened this issue Jan 22, 2009 · 13 comments
Closed

itertools.count step #49282

steve21 mannequin opened this issue Jan 22, 2009 · 13 comments
Assignees
Labels
extension-modules C modules in the Modules dir type-feature A feature request or enhancement

Comments

@steve21
Copy link
Mannequin

steve21 mannequin commented Jan 22, 2009

BPO 5032
Nosy @rhettinger, @mdickinson

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = 'https://github.com/rhettinger'
closed_at = <Date 2009-02-12.05:42:46.919>
created_at = <Date 2009-01-22.13:14:30.961>
labels = ['extension-modules', 'type-feature']
title = 'itertools.count step'
updated_at = <Date 2009-02-12.18:47:56.696>
user = 'https://bugs.python.org/steve21'

bugs.python.org fields:

activity = <Date 2009-02-12.18:47:56.696>
actor = 'LambertDW'
assignee = 'rhettinger'
closed = True
closed_date = <Date 2009-02-12.05:42:46.919>
closer = 'rhettinger'
components = ['Extension Modules']
creation = <Date 2009-01-22.13:14:30.961>
creator = 'steve21'
dependencies = []
files = []
hgrepos = []
issue_num = 5032
keywords = []
message_count = 13.0
messages = ['80368', '80384', '80605', '80609', '80614', '80618', '80643', '81725', '81736', '81745', '81748', '81758', '81800']
nosy_count = 4.0
nosy_names = ['rhettinger', 'mark.dickinson', 'LambertDW', 'steve21']
pr_nums = []
priority = 'low'
resolution = 'accepted'
stage = None
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue5032'
versions = ['Python 3.1', 'Python 2.7']

@steve21
Copy link
Mannequin Author

steve21 mannequin commented Jan 22, 2009

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()')

@steve21 steve21 mannequin added the type-feature A feature request or enhancement label Jan 22, 2009
@rhettinger rhettinger added the extension-modules C modules in the Modules dir label Jan 22, 2009
@rhettinger rhettinger self-assigned this Jan 22, 2009
@rhettinger
Copy link
Contributor

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?

@steve21
Copy link
Mannequin Author

steve21 mannequin commented Jan 27, 2009

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

@lambertdw
Copy link
Mannequin

lambertdw mannequin commented Jan 27, 2009

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

@steve21
Copy link
Mannequin Author

steve21 mannequin commented Jan 27, 2009

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..]

@lambertdw
Copy link
Mannequin

lambertdw mannequin commented Jan 27, 2009

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.

@rhettinger
Copy link
Contributor

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).

@rhettinger
Copy link
Contributor

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

@mdickinson
Copy link
Member

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.

@rhettinger
Copy link
Contributor

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.

@mdickinson
Copy link
Member

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.

@rhettinger
Copy link
Contributor

More coolness:

count(Fraction(2,3), Fraction(1,6))

@lambertdw
Copy link
Mannequin

lambertdw mannequin commented Feb 12, 2009

I run my shells with low priority so I can sneak around and kill them.

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
extension-modules C modules in the Modules dir type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

2 participants