]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Minor improvements to the itertools documentation (gh-116833)
authorRaymond Hettinger <rhettinger@users.noreply.github.com>
Thu, 14 Mar 2024 21:39:50 +0000 (16:39 -0500)
committerGitHub <noreply@github.com>
Thu, 14 Mar 2024 21:39:50 +0000 (16:39 -0500)
Doc/library/itertools.rst

index 962d23bb53bf6b40c2692c1019beff84b75e2cbb..7df8e804507ef53b06f82c9f0a3dc9905a60f352 100644 (file)
@@ -41,9 +41,9 @@ operator can be mapped across two vectors to form an efficient dot-product:
 ==================  =================       =================================================               =========================================
 Iterator            Arguments               Results                                                         Example
 ==================  =================       =================================================               =========================================
-:func:`count`       [start[, step]]         start, start+step, start+2*step, ...                            ``count(10) --> 10 11 12 13 14 ...``
-:func:`cycle`       p                       p0, p1, ... plast, p0, p1, ...                                  ``cycle('ABCD') --> A B C D A B C D ...``
-:func:`repeat`      elem [,n]               elem, elem, elem, ... endlessly or up to n times                ``repeat(10, 3) --> 10 10 10``
+:func:`count`       [start[, step]]         start, start+step, start+2*step, ...                            ``count(10)  10 11 12 13 14 ...``
+:func:`cycle`       p                       p0, p1, ... plast, p0, p1, ...                                  ``cycle('ABCD')  A B C D A B C D ...``
+:func:`repeat`      elem [,n]               elem, elem, elem, ... endlessly or up to n times                ``repeat(10, 3)  10 10 10``
 ==================  =================       =================================================               =========================================
 
 **Iterators terminating on the shortest input sequence:**
@@ -51,20 +51,20 @@ Iterator            Arguments               Results
 ============================    ============================    =================================================   =============================================================
 Iterator                        Arguments                       Results                                             Example
 ============================    ============================    =================================================   =============================================================
-:func:`accumulate`              p [,func]                       p0, p0+p1, p0+p1+p2, ...                            ``accumulate([1,2,3,4,5]) --> 1 3 6 10 15``
-:func:`batched`                 p, n                            (p0, p1, ..., p_n-1), ...                           ``batched('ABCDEFG', n=3) --> ABC DEF G``
-:func:`chain`                   p, q, ...                       p0, p1, ... plast, q0, q1, ...                      ``chain('ABC', 'DEF') --> A B C D E F``
-:func:`chain.from_iterable`     iterable                        p0, p1, ... plast, q0, q1, ...                      ``chain.from_iterable(['ABC', 'DEF']) --> A B C D E F``
-:func:`compress`                data, selectors                 (d[0] if s[0]), (d[1] if s[1]), ...                 ``compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F``
-:func:`dropwhile`               predicate, seq                  seq[n], seq[n+1], starting when predicate fails     ``dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1``
-:func:`filterfalse`             predicate, seq                  elements of seq where predicate(elem) fails         ``filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8``
+:func:`accumulate`              p [,func]                       p0, p0+p1, p0+p1+p2, ...                            ``accumulate([1,2,3,4,5])  1 3 6 10 15``
+:func:`batched`                 p, n                            (p0, p1, ..., p_n-1), ...                           ``batched('ABCDEFG', n=3)  ABC DEF G``
+:func:`chain`                   p, q, ...                       p0, p1, ... plast, q0, q1, ...                      ``chain('ABC', 'DEF')  A B C D E F``
+:func:`chain.from_iterable`     iterable                        p0, p1, ... plast, q0, q1, ...                      ``chain.from_iterable(['ABC', 'DEF'])  A B C D E F``
+:func:`compress`                data, selectors                 (d[0] if s[0]), (d[1] if s[1]), ...                 ``compress('ABCDEF', [1,0,1,0,1,1])  A C E F``
+:func:`dropwhile`               predicate, seq                  seq[n], seq[n+1], starting when predicate fails     ``dropwhile(lambda x: x<5, [1,4,6,4,1])  6 4 1``
+:func:`filterfalse`             predicate, seq                  elements of seq where predicate(elem) fails         ``filterfalse(lambda x: x%2, range(10))  0 2 4 6 8``
 :func:`groupby`                 iterable[, key]                 sub-iterators grouped by value of key(v)
-:func:`islice`                  seq, [start,] stop [, step]     elements from seq[start:stop:step]                  ``islice('ABCDEFG', 2, None) --> C D E F G``
-:func:`pairwise`                iterable                        (p[0], p[1]), (p[1], p[2])                          ``pairwise('ABCDEFG') --> AB BC CD DE EF FG``
-:func:`starmap`                 func, seq                       func(\*seq[0]), func(\*seq[1]), ...                 ``starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000``
-:func:`takewhile`               predicate, seq                  seq[0], seq[1], until predicate fails               ``takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4``
+:func:`islice`                  seq, [start,] stop [, step]     elements from seq[start:stop:step]                  ``islice('ABCDEFG', 2, None)  C D E F G``
+:func:`pairwise`                iterable                        (p[0], p[1]), (p[1], p[2])                          ``pairwise('ABCDEFG')  AB BC CD DE EF FG``
+:func:`starmap`                 func, seq                       func(\*seq[0]), func(\*seq[1]), ...                 ``starmap(pow, [(2,5), (3,2), (10,3)])  32 9 1000``
+:func:`takewhile`               predicate, seq                  seq[0], seq[1], until predicate fails               ``takewhile(lambda x: x<5, [1,4,6,4,1])  1 4``
 :func:`tee`                     it, n                           it1, it2, ... itn  splits one iterator into n
-:func:`zip_longest`             p, q, ...                       (p[0], q[0]), (p[1], q[1]), ...                     ``zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-``
+:func:`zip_longest`             p, q, ...                       (p[0], q[0]), (p[1], q[1]), ...                     ``zip_longest('ABCD', 'xy', fillvalue='-')  Ax By C- D-``
 ============================    ============================    =================================================   =============================================================
 
 **Combinatoric iterators:**
@@ -84,7 +84,7 @@ Examples                                         Results
 ``product('ABCD', repeat=2)``                    ``AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD``
 ``permutations('ABCD', 2)``                      ``AB AC AD BA BC BD CA CB CD DA DB DC``
 ``combinations('ABCD', 2)``                      ``AB AC AD BC BD CD``
-``combinations_with_replacement('ABCD', 2)``      ``AA AB AC AD BB BC BD CC CD DD``
+``combinations_with_replacement('ABCD', 2)``     ``AA AB AC AD BB BC BD CC CD DD``
 ==============================================   =============================================================
 
 
@@ -119,9 +119,9 @@ loops that truncate the stream.
 
         def accumulate(iterable, func=operator.add, *, initial=None):
             'Return running totals'
-            # accumulate([1,2,3,4,5]) --> 1 3 6 10 15
-            # accumulate([1,2,3,4,5], initial=100) --> 100 101 103 106 110 115
-            # accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120
+            # accumulate([1,2,3,4,5])  1 3 6 10 15
+            # accumulate([1,2,3,4,5], initial=100)  100 101 103 106 110 115
+            # accumulate([1,2,3,4,5], operator.mul)  1 2 6 24 120
             it = iter(iterable)
             total = initial
             if initial is None:
@@ -194,7 +194,7 @@ loops that truncate the stream.
    Roughly equivalent to::
 
       def batched(iterable, n, *, strict=False):
-          # batched('ABCDEFG', 3) --> ABC DEF G
+          # batched('ABCDEFG', 3)  ABC DEF G
           if n < 1:
               raise ValueError('n must be at least one')
           it = iter(iterable)
@@ -217,7 +217,7 @@ loops that truncate the stream.
    Roughly equivalent to::
 
       def chain(*iterables):
-          # chain('ABC', 'DEF') --> A B C D E F
+          # chain('ABC', 'DEF')  A B C D E F
           for it in iterables:
               for element in it:
                   yield element
@@ -229,7 +229,7 @@ loops that truncate the stream.
    single iterable argument that is evaluated lazily.  Roughly equivalent to::
 
       def from_iterable(iterables):
-          # chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
+          # chain.from_iterable(['ABC', 'DEF'])  A B C D E F
           for it in iterables:
               for element in it:
                   yield element
@@ -250,8 +250,8 @@ loops that truncate the stream.
    Roughly equivalent to::
 
         def combinations(iterable, r):
-            # combinations('ABCD', 2) --> AB AC AD BC BD CD
-            # combinations(range(4), 3) --> 012 013 023 123
+            # combinations('ABCD', 2)  AB AC AD BC BD CD
+            # combinations(range(4), 3)  012 013 023 123
             pool = tuple(iterable)
             n = len(pool)
             if r > n:
@@ -299,7 +299,7 @@ loops that truncate the stream.
    Roughly equivalent to::
 
         def combinations_with_replacement(iterable, r):
-            # combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
+            # combinations_with_replacement('ABC', 2)  AA AB AC BB BC CC
             pool = tuple(iterable)
             n = len(pool)
             if not n and r:
@@ -339,7 +339,7 @@ loops that truncate the stream.
    Roughly equivalent to::
 
        def compress(data, selectors):
-           # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
+           # compress('ABCDEF', [1,0,1,0,1,1])  A C E F
            return (d for d, s in zip(data, selectors) if s)
 
    .. versionadded:: 3.1
@@ -352,8 +352,8 @@ loops that truncate the stream.
    Also, used with :func:`zip` to add sequence numbers.  Roughly equivalent to::
 
       def count(start=0, step=1):
-          # count(10) --> 10 11 12 13 14 ...
-          # count(2.5, 0.5) --> 2.5 3.0 3.5 ...
+          # count(10)  10 11 12 13 14 ...
+          # count(2.5, 0.5)  2.5 3.0 3.5 ...
           n = start
           while True:
               yield n
@@ -373,7 +373,7 @@ loops that truncate the stream.
    indefinitely.  Roughly equivalent to::
 
       def cycle(iterable):
-          # cycle('ABCD') --> A B C D A B C D A B C D ...
+          # cycle('ABCD')  A B C D A B C D A B C D ...
           saved = []
           for element in iterable:
               yield element
@@ -394,7 +394,7 @@ loops that truncate the stream.
    start-up time.  Roughly equivalent to::
 
       def dropwhile(predicate, iterable):
-          # dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1
+          # dropwhile(lambda x: x<5, [1,4,6,4,1])  6 4 1
           iterable = iter(iterable)
           for x in iterable:
               if not predicate(x):
@@ -410,7 +410,7 @@ loops that truncate the stream.
    that are false. Roughly equivalent to::
 
       def filterfalse(predicate, iterable):
-          # filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8
+          # filterfalse(lambda x: x%2, range(10))  0 2 4 6 8
           if predicate is None:
               predicate = bool
           for x in iterable:
@@ -447,8 +447,8 @@ loops that truncate the stream.
    :func:`groupby` is roughly equivalent to::
 
       class groupby:
-          # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
-          # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
+          # [k for k, g in groupby('AAAABBBCCDAABBB')]  A B C D A B
+          # [list(g) for k, g in groupby('AAAABBBCCD')]  AAAA BBB CC D
 
           def __init__(self, iterable, key=None):
               if key is None:
@@ -499,10 +499,10 @@ loops that truncate the stream.
    Roughly equivalent to::
 
       def islice(iterable, *args):
-          # islice('ABCDEFG', 2) --> A B
-          # islice('ABCDEFG', 2, 4) --> C D
-          # islice('ABCDEFG', 2, None) --> C D E F G
-          # islice('ABCDEFG', 0, None, 2) --> A C E G
+          # islice('ABCDEFG', 2)  A B
+          # islice('ABCDEFG', 2, 4)  C D
+          # islice('ABCDEFG', 2, None)  C D E F G
+          # islice('ABCDEFG', 0, None, 2)  A C E G
           s = slice(*args)
           start, stop, step = s.start or 0, s.stop or sys.maxsize, s.step or 1
           it = iter(range(start, stop, step))
@@ -535,10 +535,12 @@ loops that truncate the stream.
    Roughly equivalent to::
 
         def pairwise(iterable):
-            # pairwise('ABCDEFG') --> AB BC CD DE EF FG
-            a, b = tee(iterable)
-            next(b, None)
-            return zip(a, b)
+            # pairwise('ABCDEFG') → AB BC CD DE EF FG
+            iterator = iter(iterable)
+            a = next(iterator, None)
+            for b in iterator:
+                yield a, b
+                a = b
 
    .. versionadded:: 3.10
 
@@ -562,8 +564,8 @@ loops that truncate the stream.
    Roughly equivalent to::
 
         def permutations(iterable, r=None):
-            # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
-            # permutations(range(3)) --> 012 021 102 120 201 210
+            # permutations('ABCD', 2)  AB AC AD BA BC BD CA CB CD DA DB DC
+            # permutations(range(3))  012 021 102 120 201 210
             pool = tuple(iterable)
             n = len(pool)
             r = n if r is None else r
@@ -621,8 +623,8 @@ loops that truncate the stream.
    actual implementation does not build up intermediate results in memory::
 
        def product(*args, repeat=1):
-           # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
-           # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
+           # product('ABCD', 'xy')  Ax Ay Bx By Cx Cy Dx Dy
+           # product(range(2), repeat=3)  000 001 010 011 100 101 110 111
            pools = [tuple(pool) for pool in args] * repeat
            result = [[]]
            for pool in pools:
@@ -642,7 +644,7 @@ loops that truncate the stream.
    Roughly equivalent to::
 
       def repeat(object, times=None):
-          # repeat(10, 3) --> 10 10 10
+          # repeat(10, 3)  10 10 10
           if times is None:
               while True:
                   yield object
@@ -670,7 +672,7 @@ loops that truncate the stream.
    equivalent to::
 
       def starmap(function, iterable):
-          # starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
+          # starmap(pow, [(2,5), (3,2), (10,3)])  32 9 1000
           for args in iterable:
               yield function(*args)
 
@@ -681,7 +683,7 @@ loops that truncate the stream.
    predicate is true.  Roughly equivalent to::
 
       def takewhile(predicate, iterable):
-          # takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
+          # takewhile(lambda x: x<5, [1,4,6,4,1])  1 4
           for x in iterable:
               if predicate(x):
                   yield x
@@ -741,7 +743,7 @@ loops that truncate the stream.
    Iteration continues until the longest iterable is exhausted.  Roughly equivalent to::
 
       def zip_longest(*args, fillvalue=None):
-          # zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
+          # zip_longest('ABCD', 'xy', fillvalue='-')  Ax By C- D-
           iterators = [iter(it) for it in args]
           num_active = len(iterators)
           if not num_active:
@@ -816,7 +818,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
 
    def prepend(value, iterable):
        "Prepend a single value in front of an iterable."
-       # prepend(1, [2, 3, 4]) --> 1 2 3 4
+       # prepend(1, [2, 3, 4])  1 2 3 4
        return chain([value], iterable)
 
    def tabulate(function, start=0):
@@ -842,7 +844,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
 
    def tail(n, iterable):
        "Return an iterator over the last n items."
-       # tail(3, 'ABCDEFG') --> E F G
+       # tail(3, 'ABCDEFG')  E F G
        return iter(collections.deque(iterable, maxlen=n))
 
    def consume(iterator, n=None):
@@ -865,26 +867,27 @@ and :term:`generators <generator>` which incur interpreter overhead.
 
    def first_true(iterable, default=False, predicate=None):
        "Returns the first true value or the *default* if there is no true value."
-       # first_true([a,b,c], x) --> a or b or c or x
-       # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
+       # first_true([a,b,c], x)  a or b or c or x
+       # first_true([a,b], x, f)  a if f(a) else b if f(b) else x
        return next(filter(predicate, iterable), default)
 
    def all_equal(iterable, key=None):
        "Returns True if all the elements are equal to each other."
+       # all_equal('4٤໔4৪', key=int) → True
        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
+       # 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
-       # unique_everseen('ABBcCAD', str.casefold) --> A B c D
+       # unique_everseen('AAAABBBCCDAABBB')  A B C D
+       # unique_everseen('ABBcCAD', str.casefold)  A B c D
        seen = set()
        if key is None:
            for element in filterfalse(seen.__contains__, iterable):
@@ -899,7 +902,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
 
    def sliding_window(iterable, n):
        "Collect data into overlapping fixed-length chunks or blocks."
-       # sliding_window('ABCDEFG', 4) --> ABCD BCDE CDEF DEFG
+       # sliding_window('ABCDEFG', 4)  ABCD BCDE CDEF DEFG
        it = iter(iterable)
        window = collections.deque(islice(it, n-1), maxlen=n)
        for x in it:
@@ -908,9 +911,9 @@ and :term:`generators <generator>` which incur interpreter overhead.
 
    def grouper(iterable, n, *, incomplete='fill', fillvalue=None):
        "Collect data into non-overlapping fixed-length chunks or blocks."
-       # grouper('ABCDEFG', 3, fillvalue='x') --> ABC DEF Gxx
-       # grouper('ABCDEFG', 3, incomplete='strict') --> ABC DEF ValueError
-       # grouper('ABCDEFG', 3, incomplete='ignore') --> ABC DEF
+       # grouper('ABCDEFG', 3, fillvalue='x')  ABC DEF Gxx
+       # grouper('ABCDEFG', 3, incomplete='strict')  ABC DEF ValueError
+       # grouper('ABCDEFG', 3, incomplete='ignore')  ABC DEF
        iterators = [iter(iterable)] * n
        match incomplete:
            case 'fill':
@@ -924,7 +927,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
 
    def roundrobin(*iterables):
        "Visit input iterables in a cycle until each is exhausted."
-       # roundrobin('ABC', 'D', 'EF') --> A D E B F C
+       # roundrobin('ABC', 'D', 'EF')  A D E B F C
        # Algorithm credited to George Sakkis
        iterators = map(iter, iterables)
        for num_active in range(len(iterables), 0, -1):
@@ -936,19 +939,19 @@ and :term:`generators <generator>` which incur interpreter overhead.
 
        If *predicate* is slow, consider wrapping it with functools.lru_cache().
        """
-       # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
+       # partition(is_odd, range(10))  0 2 4 6 8   and  1 3 5 7 9
        t1, t2 = tee(iterable)
        return filterfalse(predicate, t1), filter(predicate, t2)
 
    def subslices(seq):
        "Return all contiguous non-empty subslices of a sequence."
-       # subslices('ABCD') --> A AB ABC ABCD B BC BCD C CD D
+       # subslices('ABCD')  A AB ABC ABCD B BC BCD C CD D
        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
+       # iter_index('AABCADEAF', 'A')  0 1 4 7
        seq_index = getattr(iterable, 'index', None)
        if seq_index is None:
            # Path for general iterables
@@ -972,7 +975,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
 
        Converts a call-until-exception interface to an iterator interface.
        """
-       # iter_except(d.popitem, KeyError) --> non-blocking dictionary iterator
+       # iter_except(d.popitem, KeyError)  non-blocking dictionary iterator
        try:
            if first is not None:
                yield first()
@@ -987,28 +990,28 @@ The following recipes have a more mathematical flavor:
 .. testcode::
 
    def powerset(iterable):
-       "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
+       "powerset([1,2,3])  () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
        s = list(iterable)
        return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
 
    def sum_of_squares(iterable):
        "Add up the squares of the input values."
-       # sum_of_squares([10, 20, 30]) --> 1400
+       # sum_of_squares([10, 20, 30])  1400
        return math.sumprod(*tee(iterable))
 
    def reshape(matrix, cols):
        "Reshape a 2-D matrix to have a given number of columns."
-       # reshape([(0, 1), (2, 3), (4, 5)], 3) -->  (0, 1, 2), (3, 4, 5)
+       # reshape([(0, 1), (2, 3), (4, 5)], 3)   (0, 1, 2), (3, 4, 5)
        return batched(chain.from_iterable(matrix), cols, strict=True)
 
    def transpose(matrix):
        "Swap the rows and columns of a 2-D matrix."
-       # transpose([(1, 2, 3), (11, 22, 33)]) --> (1, 11) (2, 22) (3, 33)
+       # transpose([(1, 2, 3), (11, 22, 33)])  (1, 11) (2, 22) (3, 33)
        return zip(*matrix, strict=True)
 
    def matmul(m1, m2):
        "Multiply two matrices."
-       # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) --> (49, 80), (41, 60)
+       # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)])  (49, 80), (41, 60)
        n = len(m2[0])
        return batched(starmap(math.sumprod, product(m1, transpose(m2))), n)
 
@@ -1023,10 +1026,10 @@ The following recipes have a more mathematical flavor:
        Article:  https://betterexplained.com/articles/intuitive-convolution/
        Video:    https://www.youtube.com/watch?v=KuXjwB4LzSA
        """
-       # convolve([1, -1, -20], [1, -3]) --> 1 -4 -17 60
-       # convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur)
-       # convolve(data, [1/2, 0, -1/2]) --> 1st derivative estimate
-       # convolve(data, [1, -2, 1]) --> 2nd derivative estimate
+       # convolve([1, -1, -20], [1, -3])  1 -4 -17 60
+       # convolve(data, [0.25, 0.25, 0.25, 0.25])  Moving average (blur)
+       # convolve(data, [1/2, 0, -1/2])  1st derivative estimate
+       # convolve(data, [1, -2, 1])  2nd derivative estimate
        kernel = tuple(kernel)[::-1]
        n = len(kernel)
        padded_signal = chain(repeat(0, n-1), signal, repeat(0, n-1))
@@ -1038,7 +1041,7 @@ The following recipes have a more mathematical flavor:
 
           (x - 5) (x + 4) (x - 3)  expands to:   x³ -4x² -17x + 60
        """
-       # polynomial_from_roots([5, -4, 3]) --> [1, -4, -17, 60]
+       # polynomial_from_roots([5, -4, 3])  [1, -4, -17, 60]
        factors = zip(repeat(1), map(operator.neg, roots))
        return list(functools.reduce(convolve, factors, [1]))
 
@@ -1048,7 +1051,7 @@ The following recipes have a more mathematical flavor:
        Computes with better numeric stability than Horner's method.
        """
        # Evaluate x³ -4x² -17x + 60 at x = 5
-       # polynomial_eval([1, -4, -17, 60], x=5) --> 0
+       # polynomial_eval([1, -4, -17, 60], x=5)  0
        n = len(coefficients)
        if not n:
            return type(x)(0)
@@ -1061,14 +1064,14 @@ The following recipes have a more mathematical flavor:
           f(x)  =  x³ -4x² -17x + 60
           f'(x) = 3x² -8x  -17
        """
-       # polynomial_derivative([1, -4, -17, 60]) --> [3, -8, -17]
+       # polynomial_derivative([1, -4, -17, 60])  [3, -8, -17]
        n = len(coefficients)
        powers = reversed(range(1, n))
        return list(map(operator.mul, coefficients, powers))
 
    def sieve(n):
        "Primes less than n."
-       # sieve(30) --> 2 3 5 7 11 13 17 19 23 29
+       # sieve(30)  2 3 5 7 11 13 17 19 23 29
        if n > 2:
            yield 2
        start = 3
@@ -1082,9 +1085,9 @@ The following recipes have a more mathematical flavor:
 
    def factor(n):
        "Prime factors of n."
-       # factor(99) --> 3 3 11
-       # factor(1_000_000_000_000_007) --> 47 59 360620266859
-       # factor(1_000_000_000_000_403) --> 1000000000000403
+       # factor(99)  3 3 11
+       # factor(1_000_000_000_000_007)  47 59 360620266859
+       # factor(1_000_000_000_000_403)  1000000000000403
        for prime in sieve(math.isqrt(n) + 1):
            while not n % prime:
                yield prime
@@ -1097,7 +1100,7 @@ The following recipes have a more mathematical flavor:
    def totient(n):
        "Count of natural numbers up to n that are coprime to n."
        # https://mathworld.wolfram.com/TotientFunction.html
-       # totient(12) --> 4 because len([1, 5, 7, 11]) == 4
+       # totient(12)  4 because len([1, 5, 7, 11]) == 4
        for p in unique_justseen(factor(n)):
            n -= n // p
        return n
@@ -1567,6 +1570,13 @@ The following recipes have a more mathematical flavor:
     >>> ranges = [range(5, 1000), range(4, 3000), range(0), range(3, 2000), range(2, 5000), range(1, 3500)]
     >>> collections.Counter(roundrobin(ranges)) == collections.Counter(ranges)
     True
+    >>> # Verify that the inputs are consumed lazily
+    >>> input_iterators = list(map(iter, ['abcd', 'ef', '', 'ghijk', 'l', 'mnopqr']))
+    >>> output_iterator = roundrobin(*input_iterators)
+    >>> ''.join(islice(output_iterator, 10))
+    'aeglmbfhnc'
+    >>> ''.join(chain(*input_iterators))
+    'dijkopqr'
 
     >>> def is_odd(x):
     ...     return x % 2 == 1
@@ -1676,7 +1686,7 @@ The following recipes have a more mathematical flavor:
 
     def triplewise(iterable):
         "Return overlapping triplets from an iterable"
-        # triplewise('ABCDEFG') --> ABC BCD CDE DEF EFG
+        # triplewise('ABCDEFG')  ABC BCD CDE DEF EFG
         for (a, _), (b, c) in pairwise(pairwise(iterable)):
             yield a, b, c