|
25 | 25 | "cauchyprod", "diagonal_reduce", |
26 | 26 | "fibonacci", "primes"] |
27 | 27 |
|
28 | | -from itertools import repeat, chain, count, product, takewhile |
| 28 | +from threading import RLock |
| 29 | +from itertools import repeat, chain, count, takewhile |
29 | 30 | from operator import add as primitive_add, mul as primitive_mul, \ |
30 | 31 | pow as primitive_pow, mod as primitive_mod, \ |
31 | 32 | floordiv as primitive_floordiv, truediv as primitive_truediv, \ |
@@ -722,17 +723,32 @@ def fibonacci(): |
722 | 723 | yield a |
723 | 724 | a, b = b, a + b |
724 | 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() |
725 | 730 | def primes(): |
726 | 731 | """Return the prime numbers 2, 3, 5, 7, 11, 13, ... as a lazy sequence. |
727 | 732 |
|
728 | 733 | Implementation is based on an FP version of the sieve of Eratosthenes, |
729 | 734 | with internal memoization. |
730 | 735 | """ |
731 | | - memo = [] # we memoize manually for speed (see test_gmemo.py) |
| 736 | + # TODO: utilize the memo also when already in the loop. |
732 | 737 | def memoprimes(): |
733 | | - for n in chain([2, 3, 5, 7], (sum(xs) for k in count(10, step=10) |
734 | | - for xs in product([k], [1, 3, 7, 9]))): |
735 | | - if not any(n % p == 0 for p in takewhile(lambda x: x*x <= n, memo)): |
736 | | - memo.append(n) |
| 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) |
737 | 753 | yield n |
738 | 754 | return m(memoprimes()) |
0 commit comments