From 48d99dfcedcdca5d870de42fc3d9d642edfcea25 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 13 Mar 2024 14:16:51 -0500 Subject: [PATCH 1/6] justseen() before everseen(). move iter_index() down. --- Doc/library/itertools.rst | 58 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 26d5cdaff8c14f..c7751151cea21e 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -873,6 +873,14 @@ which incur interpreter overhead. "Returns True if all the elements are equal to each other." return len(take(2, groupby(iterable, key))) <= 1 + def unique_justseen(iterable, key=None): + "List unique elements, preserving order. Remember only the element just seen." + # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B + # unique_justseen('ABBcCAD', str.casefold) --> A B c A D + if key is None: + return map(operator.itemgetter(0), groupby(iterable)) + return map(next, map(operator.itemgetter(1), groupby(iterable, key))) + def unique_everseen(iterable, key=None): "List unique elements, preserving order. Remember all elements ever seen." # unique_everseen('AAAABBBCCDAABBB') --> A B C D @@ -889,35 +897,6 @@ which incur interpreter overhead. seen.add(k) yield element - def unique_justseen(iterable, key=None): - "List unique elements, preserving order. Remember only the element just seen." - # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B - # unique_justseen('ABBcCAD', str.casefold) --> A B c A D - if key is None: - return map(operator.itemgetter(0), groupby(iterable)) - return map(next, map(operator.itemgetter(1), groupby(iterable, key))) - - def iter_index(iterable, value, start=0, stop=None): - "Return indices where a value occurs in a sequence or iterable." - # iter_index('AABCADEAF', 'A') --> 0 1 4 7 - seq_index = getattr(iterable, 'index', None) - if seq_index is None: - # Path for general iterables - it = islice(iterable, start, stop) - for i, element in enumerate(it, start): - if element is value or element == value: - yield i - else: - # Path for sequences with an index() method - stop = len(iterable) if stop is None else stop - i = start - try: - while True: - yield (i := seq_index(value, i, stop)) - i += 1 - except ValueError: - pass - def sliding_window(iterable, n): "Collect data into overlapping fixed-length chunks or blocks." # sliding_window('ABCDEFG', 4) --> ABCD BCDE CDEF DEFG @@ -967,6 +946,27 @@ which incur interpreter overhead. slices = starmap(slice, combinations(range(len(seq) + 1), 2)) return map(operator.getitem, repeat(seq), slices) + def iter_index(iterable, value, start=0, stop=None): + "Return indices where a value occurs in a sequence or iterable." + # iter_index('AABCADEAF', 'A') --> 0 1 4 7 + seq_index = getattr(iterable, 'index', None) + if seq_index is None: + # Path for general iterables + it = islice(iterable, start, stop) + for i, element in enumerate(it, start): + if element is value or element == value: + yield i + else: + # Path for sequences with an index() method + stop = len(iterable) if stop is None else stop + i = start + try: + while True: + yield (i := seq_index(value, i, stop)) + i += 1 + except ValueError: + pass + def iter_except(func, exception, first=None): """ Call a function repeatedly until an exception is raised. From acd02be69d7c525c953cf7b7d72824fbbd3d619d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 13 Mar 2024 14:30:23 -0500 Subject: [PATCH 2/6] Add sieve() to the incubator list --- Doc/library/itertools.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index c7751151cea21e..7e5e32286add8c 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -785,8 +785,8 @@ well as with the built-in itertools such as ``map()``, ``filter()``, A secondary purpose of the recipes is to serve as an incubator. The ``accumulate()``, ``compress()``, and ``pairwise()`` itertools started out as -recipes. Currently, the ``sliding_window()`` and ``iter_index()`` recipes -are being tested to see whether they prove their worth. +recipes. Currently, the ``sliding_window()``, ``iter_index()``, and ``sieve()`` +recipes are being tested to see whether they prove their worth. Substantially all of these recipes and many, many others can be installed from the `more-itertools project `_ found From 7893babd7a93c5879b83de24fe0e4fd64e5677b8 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 13 Mar 2024 14:33:44 -0500 Subject: [PATCH 3/6] Add a link for "functional style" --- Doc/library/itertools.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 7e5e32286add8c..221fcdc9cf80aa 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -795,12 +795,12 @@ on the Python Package Index:: python -m pip install more-itertools Many of the recipes offer the same high performance as the underlying toolset. -Superior memory performance is kept by processing elements one at a time -rather than bringing the whole iterable into memory all at once. Code volume is -kept small by linking the tools together in a functional style which helps -eliminate temporary variables. High speed is retained by preferring -"vectorized" building blocks over the use of for-loops and :term:`generator`\s -which incur interpreter overhead. +Superior memory performance is kept by processing elements one at a time rather +than bringing the whole iterable into memory all at once. Code volume is kept +small by linking the tools together in a `functional style +`_. High speed +is retained by preferring "vectorized" building blocks over the use of for-loops +and :term:`generator`\s which incur interpreter overhead. .. testcode:: From 20327fdd260b486f3960d885302d786f71449b17 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 13 Mar 2024 14:41:22 -0500 Subject: [PATCH 4/6] Change the polynomial_eval() example match the previous example --- Doc/library/itertools.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 221fcdc9cf80aa..c8d7ab7be122ec 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -1047,8 +1047,8 @@ The following recipes have a more mathematical flavor: Computes with better numeric stability than Horner's method. """ - # Evaluate x³ -4x² -17x + 60 at x = 2.5 - # polynomial_eval([1, -4, -17, 60], x=2.5) --> 8.125 + # Evaluate x³ -4x² -17x + 60 at x = 5 + # polynomial_eval([1, -4, -17, 60], x=5) --> 0 n = len(coefficients) if not n: return type(x)(0) From 6298993cd72cdfbd233271a4e3bfcc58388aad39 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 13 Mar 2024 14:48:20 -0500 Subject: [PATCH 5/6] Update Doc/library/itertools.rst Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/library/itertools.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index c8d7ab7be122ec..95023088269cd9 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -800,7 +800,7 @@ than bringing the whole iterable into memory all at once. Code volume is kept small by linking the tools together in a `functional style `_. High speed is retained by preferring "vectorized" building blocks over the use of for-loops -and :term:`generator`\s which incur interpreter overhead. +and :term:`generators ` which incur interpreter overhead. .. testcode:: From d85ccebcbaa784461e6dd165aea363193e2917fd Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 13 Mar 2024 14:47:09 -0500 Subject: [PATCH 6/6] Update test to match the example --- Doc/library/itertools.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 95023088269cd9..962d23bb53bf6b 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -1311,10 +1311,10 @@ The following recipes have a more mathematical flavor: >>> from fractions import Fraction >>> from decimal import Decimal - >>> polynomial_eval([1, -4, -17, 60], x=2) - 18 - >>> x = 2; x**3 - 4*x**2 -17*x + 60 - 18 + >>> polynomial_eval([1, -4, -17, 60], x=5) + 0 + >>> x = 5; x**3 - 4*x**2 -17*x + 60 + 0 >>> polynomial_eval([1, -4, -17, 60], x=2.5) 8.125 >>> x = 2.5; x**3 - 4*x**2 -17*x + 60