Skip to content

Commit bd0d392

Browse files
committed
primes: avoid reinventing the gmemoize wheel
1 parent a3f5a55 commit bd0d392

File tree

2 files changed

+17
-35
lines changed

2 files changed

+17
-35
lines changed

unpythonic/mathseq.py

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
"cauchyprod", "diagonal_reduce",
2626
"fibonacci", "primes"]
2727

28-
from threading import RLock
2928
from itertools import repeat, chain, count, takewhile
3029
from operator import add as primitive_add, mul as primitive_mul, \
3130
pow as primitive_pow, mod as primitive_mod, \
@@ -34,7 +33,7 @@
3433
neg as primitive_neg, pos as primitive_pos
3534

3635
from .it import take, rev
37-
from .gmemo import imemoize
36+
from .gmemo import imemoize, gmemoize
3837

3938
# stuff to support float, mpf and SymPy expressions transparently
4039
#
@@ -718,37 +717,23 @@ def diagonal():
718717

719718
def fibonacci():
720719
"""Return the Fibonacci numbers 1, 1, 2, 3, 5, 8, ... as a lazy sequence."""
721-
a, b = 1, 1
722-
while True:
723-
yield a
724-
a, b = b, a + b
725-
726-
# Manual memoization for speed. One global memo to save memory when several
727-
# instances of primes() are created in the same session.
728-
_primesmemo = [2, 3]
729-
_primeslock = RLock()
720+
def fibos():
721+
a, b = 1, 1
722+
while True:
723+
yield a
724+
a, b = b, a + b
725+
return m(fibos())
726+
727+
@gmemoize
728+
def _primes(): # at the top level; we want only one global memo shared across all instances
729+
yield 2
730+
for n in chain([3, 5, 7], (d + k for d in count(10, step=10)
731+
for k in [1, 3, 7, 9])):
732+
if not any(n % p == 0 for p in takewhile(lambda x: x*x <= n, _primes())):
733+
yield n
730734
def primes():
731735
"""Return the prime numbers 2, 3, 5, 7, 11, 13, ... as a lazy sequence.
732736
733-
Implementation is based on an FP version of the sieve of Eratosthenes,
734-
with internal memoization.
737+
FP sieve of Eratosthenes with memoization.
735738
"""
736-
# TODO: utilize the memo also when already in the loop.
737-
def memoprimes():
738-
# to avoid a race condition, we must know the last p actually yielded by us
739-
# (since some other thread may insert a new entry to _primesmemo between
740-
# our last yield and when we read _primesmemo[-1] for the divmod check)
741-
for p in _primesmemo:
742-
yield p
743-
# find the next candidate
744-
decade, mod = divmod(p, 10)
745-
inits = range(mod + 2, 10, 2) if mod < 9 else []
746-
inits = (10*decade + k for k in inits)
747-
for n in chain(inits, (d + k for d in count(10*(decade + 1), step=10)
748-
for k in [1, 3, 7, 9])):
749-
if not any(n % p == 0 for p in takewhile(lambda x: x*x <= n, _primesmemo)):
750-
with _primeslock: # lock to make test-and-update atomic.
751-
if _primesmemo[-1] < n:
752-
_primesmemo.append(n)
753-
yield n
754-
return m(memoprimes())
739+
return m(_primes())

unpythonic/test/test_mathseq.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,6 @@ def ulp(x): # Unit in the Last Place
219219
assert tuple(take(10, primes())) == (2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
220220
assert tuple(take(10, fibonacci())) == (1, 1, 2, 3, 5, 8, 13, 21, 34, 55)
221221

222-
assert tuple(take(20, primes())) == tuple(take(20, primes())) # caching
223-
assert last(take(21, primes())) == 73
224-
225222
print("All tests PASSED")
226223

227224
if __name__ == '__main__':

0 commit comments

Comments
 (0)