Is There an Expression for an Infinite Iterator

Is there an expression for an infinite iterator?

for x in iter(int, 1): pass
  • Two-argument iter = zero-argument callable + sentinel value
  • int() always returns 0

Therefore, iter(int, 1) is an infinite iterator. There are obviously a huge number of variations on this particular theme (especially once you add lambda into the mix). One variant of particular note is iter(f, object()), as using a freshly created object as the sentinel value almost guarantees an infinite iterator regardless of the callable used as the first argument.

combine infinite with finite iterator

You can create a generator using a comprehension. Comprehensions support iterating over multiple iterables at once using multiple fors

for j, q in ((j, q) for j in itertools.count() for q in Q):
...

How to create an infinite iterator to generate an incrementing alphabet pattern?

Yield column_names's last element every time, and use itertools.count instead of range to provide infinite increase:

import itertools

def _column_name_generator():
column_names = []
for x in itertools.count():
if x < 26:
column_names.append(string.ascii_uppercase[x % 26])
else:
column_names.append(column_names[x/26 - 1] + string.ascii_uppercase[x % 26])
yield column_names[-1]

A better solution, altering the original code but dismissing the need in a memory consuming list of column_names, would be

import itertools, string

def _column_name_generator():
for i in itertools.count(1):
for p in itertools.product(string.ascii_uppercase, repeat=i):
yield ''.join(p)

it basically iterates over the product of length i the uppercase ascii letters (every sequence possible) when i is gradually increasing, starting from 1 (A, B, C).

Infinite loops using 'for' in Python

range is a class, and using in like e.g. range(1, a) creates an object of that class. This object is created only once, it is not recreated every iteration of the loop. That's the reason the first example will not result in an infinite loop.

The other two loops are not infinite because, unlike the range object, the loop variable i is recreated (or rather reinitialized) each iteration. The values you assign to i inside the loop will be overwritten as the loop iterates.

Looping from 1 to infinity in Python

Using itertools.count:

import itertools
for i in itertools.count(start=1):
if there_is_a_reason_to_break(i):
break

In Python 2, range() and xrange() were limited to sys.maxsize. In Python 3 range() can go much higher, though not to infinity:

import sys
for i in range(sys.maxsize**10): # you could go even higher if you really want
if there_is_a_reason_to_break(i):
break

So it's probably best to use count().

How to stop an infinite iterator on the first None value?

flat_map() is the wrong tool for this task. It is designed to flatten an iterator of iterators, i.e. whenever it encounters None on an inner iterator, its job is to switch to the next iterator. If you keep feeding it empty iterators, it will keep looping until it finds a non-empty one to deliver values from. flat_map() will only terminate iteration when the outer iterator does, which in your case never happens.

What you need is two things: first, mark the end of iteration in some way other than an empty iterator - e.g. by using an Option where Some means "here is another iterator for you" and None means "I'm no longer interested in iteration, you can terminate". Second, you need to terminate the iteration before the flattening step, using an adapter with the capacity to do so, such as take_while() or scan().

Here is a modification of your code that terminates after i reaches 10:

fn main() {
let iter = (0..)
.map(move |i| if i < 10 { Some(0..i) } else { None })
.scan((), |_, item| item)
.flatten();
for i in iter {
println!("{}", i);
}
}

Playground

Here the flat_map() is broken into three parts: first a map() that produces Some(inner_iterator) while we want to iterate, and None when we no longer do. Then comes scan() that transforms the iterator into one whose next() just returns the values returned by the closure. Since the closure returns item (inner iterator wrapped in an option) unchanged, Some(inner_iterator) will pass through, and None will be propagated as the end-of-iteration signal. Finally, flatten() funnels the items produced by inner iterators into a single iterator, as flat_map() did in your original code.

And if you really want to terminate on the first inner iterator that is empty, that can be arranged as well, by extracting the first value in scan() and testing it:

// using 1.. to avoid immediately terminating on 0..0
let iter = (1..)
.map(move |i| if i < 10 { 0..i } else { 0..0 })
.scan((), |_, mut inner| match inner.next() {
Some(first) => Some(once(first).chain(inner)),
None => None,
})
.flatten();

Playground

Note that fuse() is not what you want in any of this. Its purpose is to allow the iterator to be safely queried after exhaustion. Normally an iterator could panic if you call next() after it has already returned None. fuse() extends the iterator contract to allow calling next() after it has already returned None. This is implemented with a separate flag along the inner iterator, and a check of that flag in next(). If you don't query the iterator after it has returned None—which a for loop doesn't—then you have no need for fuse().



Related Topics



Leave a reply



Submit