Last active
October 27, 2015 14:09
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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