|
25 | 25 | "cauchyprod", "diagonal_reduce", |
26 | 26 | "fibonacci", "primes"] |
27 | 27 |
|
28 | | -from threading import RLock |
29 | 28 | from itertools import repeat, chain, count, takewhile |
30 | 29 | from operator import add as primitive_add, mul as primitive_mul, \ |
31 | 30 | pow as primitive_pow, mod as primitive_mod, \ |
|
34 | 33 | neg as primitive_neg, pos as primitive_pos |
35 | 34 |
|
36 | 35 | from .it import take, rev |
37 | | -from .gmemo import imemoize |
| 36 | +from .gmemo import imemoize, gmemoize |
38 | 37 |
|
39 | 38 | # stuff to support float, mpf and SymPy expressions transparently |
40 | 39 | # |
@@ -718,37 +717,23 @@ def diagonal(): |
718 | 717 |
|
719 | 718 | def fibonacci(): |
720 | 719 | """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 |
730 | 734 | def primes(): |
731 | 735 | """Return the prime numbers 2, 3, 5, 7, 11, 13, ... as a lazy sequence. |
732 | 736 |
|
733 | | - Implementation is based on an FP version of the sieve of Eratosthenes, |
734 | | - with internal memoization. |
| 737 | + FP sieve of Eratosthenes with memoization. |
735 | 738 | """ |
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()) |
0 commit comments