Minor improvements to the itertools documentation (gh-116833)

This commit is contained in:
Raymond Hettinger 2024-03-14 16:39:50 -05:00 committed by GitHub
parent c432df6d56
commit ab9e322ae1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 94 additions and 84 deletions

View 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٤໔', 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