Skip to content

Instantly share code, notes, and snippets.

@rbaron
Last active October 27, 2015 14:09
Show Gist options
  • Save rbaron/0daaaf0e8e820a72466a to your computer and use it in GitHub Desktop.
Save rbaron/0daaaf0e8e820a72466a to your computer and use it in GitHub Desktop.
Two ways of generating an infinite stream of prime numbers via sieve of Eratosthenes
import itertools
def take(n, iterable):
return list(itertools.islice(iterable, n))
def enum_integers(start):
return itertools.count(start)
def is_not_divisible_by(n, which):
return n%which != 0
def recursive_prime_gen():
gen = enum_integers(start=2)
def yield_sieve(gen):
head = next(gen)
yield head
# Filter out multiples of `head` from next generator
gen = itertools.ifilter(lambda e: is_not_divisible_by(e, head), gen)
for v in yield_sieve(gen):
yield v
return yield_sieve(gen)
def iterative_prime_gen():
gen = enum_integers(start=2)
while True:
head = next(gen)
yield head
# Filter out multiples of `head` from next generator
gen = itertools.ifilter(lambda e: is_not_divisible_by(e, head), gen)
print "Recursive: ", take(10, recursive_prime_gen())
# Recursive: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
# ^ Expected result
print "Iterative: ", take(10, iterative_prime_gen())
# Iterative: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
# ^ Not really what I expected
# Initially I thought we can't for some reason update the value of `gen`
# in the iterative version, but that is just not true:
def iterative_multiples_of_5_gen():
gen = enum_integers(start=5)
while True:
head = next(gen)
yield head
# Filter out multiples of 5 from next generator
gen = itertools.ifilter(lambda e: not is_not_divisible_by(e, 5), gen)
print "Iterative multiples of 5: ", take(10, iterative_multiples_of_5_gen())
# Iterative multiples of 5: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
# The problem is analogous to:
import sys
funcs = []
for i in xrange(5):
funcs.append(lambda: sys.stdout.write("Func #{}\n".format(i)))
for func in funcs:
func()
# Prints:
# Func #4
# Func #4
# Func #4
# Func #4
# Func #4
# Fixing it is as easy as creating a binding upon defining each
# lambda. It can be done by passing `i` as an argument:
funcs = []
for i in xrange(5):
funcs.append(lambda i=i: sys.stdout.write("Func #{}\n".format(i)))
for func in funcs:
func()
# Prints
# Func #0
# Func #1
# Func #2
# Func #3
# Func #4
# Let's not try to fix the iterative version with this idea:
def iterative_prime_gen_fixed():
gen = enum_integers(start=2)
while True:
head = next(gen)
yield head
# Filter out multiples of `head` from next generator
gen = itertools.ifilter(lambda e, head=head: is_not_divisible_by(e, head), gen)
print "Iterative with fix: ", take(10, iterative_prime_gen_fixed())
# Iterative whit fix: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment