@@ -47,7 +47,7 @@ Iterator Arguments Results
4747Iterator Arguments Results Example
4848============================ ============================ ================================================= =============================================================
4949:func: `accumulate ` p [,func] p0, p0+p1, p0+p1+p2, ... ``accumulate([1,2,3,4,5]) → 1 3 6 10 15 ``
50- :func: `batched ` p, n (p0, p1, ..., p_n-1), ... ``batched('ABCDEFG', n=2 ) → AB CD EF G ``
50+ :func: `batched ` p, n (p0, p1, ..., p_n-1), ... ``batched('ABCDEFG', n=3 ) → ABC DEF G ``
5151:func: `chain ` p, q, ... p0, p1, ... plast, q0, q1, ... ``chain('ABC', 'DEF') → A B C D E F ``
5252:func: `chain.from_iterable ` iterable p0, p1, ... plast, q0, q1, ... ``chain.from_iterable(['ABC', 'DEF']) → A B C D E F ``
5353: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 ``
@@ -181,7 +181,7 @@ loops that truncate the stream.
181181 Roughly equivalent to::
182182
183183 def batched(iterable, n, *, strict=False):
184- # batched('ABCDEFG', 2 ) → AB CD EF G
184+ # batched('ABCDEFG', 3 ) → ABC DEF G
185185 if n < 1:
186186 raise ValueError('n must be at least one')
187187 iterator = iter(iterable)
@@ -819,7 +819,7 @@ well as with the built-in itertools such as ``map()``, ``filter()``,
819819
820820A secondary purpose of the recipes is to serve as an incubator. The
821821``accumulate() ``, ``compress() ``, and ``pairwise() `` itertools started out as
822- recipes. Currently, the ``sliding_window() ``, ``iter_index () ``, and ``sieve() ``
822+ recipes. Currently, the ``sliding_window() ``, ``derangements () ``, and ``sieve() ``
823823recipes are being tested to see whether they prove their worth.
824824
825825Substantially all of these recipes and many, many others can be installed from
@@ -838,11 +838,16 @@ and :term:`generators <generator>` which incur interpreter overhead.
838838
839839.. testcode ::
840840
841+ from itertools import (accumulate, batched, chain, combinations, compress,
842+ count, cycle, filterfalse, groupby, islice, permutations, product,
843+ repeat, starmap, tee, zip_longest)
841844 from collections import Counter, deque
842845 from contextlib import suppress
843846 from functools import reduce
844- from math import comb, prod, sumprod, isqrt
845- from operator import itemgetter, getitem, mul, neg
847+ from math import comb, isqrt, prod, sumprod
848+ from operator import getitem, is_not, itemgetter, mul, neg
849+
850+ # ==== Basic one liners ====
846851
847852 def take(n, iterable):
848853 "Return first n items of the iterable as a list."
@@ -899,15 +904,17 @@ and :term:`generators <generator>` which incur interpreter overhead.
899904
900905 def first_true(iterable, default=False, predicate=None):
901906 "Returns the first true value or the *default * if there is no true value."
902- # first_true([a,b, c], x) → a or b or c or x
903- # first_true([a,b], x, f) → a if f(a) else b if f(b) else x
907+ # first_true([a, b, c], x) → a or b or c or x
908+ # first_true([a, b], x, f) → a if f(a) else b if f(b) else x
904909 return next(filter(predicate, iterable), default)
905910
906911 def all_equal(iterable, key=None):
907912 "Returns True if all the elements are equal to each other."
908913 # all_equal('4٤௪౪໔', key=int) → True
909914 return len(take(2, groupby(iterable, key))) <= 1
910915
916+ # ==== Data pipelines ====
917+
911918 def unique_justseen(iterable, key=None):
912919 "Yield unique elements, preserving order. Remember only the element just seen."
913920 # unique_justseen('AAAABBBCCDAABBB') → A B C D A B
@@ -940,7 +947,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
940947
941948 def sliding_window(iterable, n):
942949 "Collect data into overlapping fixed-length chunks or blocks."
943- # sliding_window('ABCDEFG', 4 ) → ABCD BCDE CDEF DEFG
950+ # sliding_window('ABCDEFG', 3 ) → ABC BCD CDE DEF EFG
944951 iterator = iter(iterable)
945952 window = deque(islice(iterator, n - 1), maxlen=n)
946953 for x in iterator:
@@ -949,7 +956,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
949956
950957 def grouper(iterable, n, *, incomplete='fill', fillvalue=None):
951958 "Collect data into non-overlapping fixed-length chunks or blocks."
952- # grouper('ABCDEFG', 3, fillvalue='x') → ABC DEF Gxx
959+ # grouper('ABCDEFG', 3, fillvalue='x') → ABC DEF Gxx
953960 # grouper('ABCDEFG', 3, incomplete='strict') → ABC DEF ValueError
954961 # grouper('ABCDEFG', 3, incomplete='ignore') → ABC DEF
955962 iterators = [iter(iterable)] * n
@@ -978,6 +985,16 @@ and :term:`generators <generator>` which incur interpreter overhead.
978985 slices = starmap(slice, combinations(range(len(seq) + 1), 2))
979986 return map(getitem, repeat(seq), slices)
980987
988+ def derangements(iterable, r=None):
989+ "Produce r length permutations without fixed points."
990+ # derangements('ABCD') → BADC BCDA BDAC CADB CDAB CDBA DABC DCAB DCBA
991+ # Algorithm credited to Stefan Pochmann
992+ seq = tuple(iterable)
993+ pos = tuple(range(len(seq)))
994+ have_moved = map(map, repeat(is_not), repeat(pos), permutations(pos, r=r))
995+ valid_derangements = map(all, have_moved)
996+ return compress(permutations(seq, r=r), valid_derangements)
997+
981998 def iter_index(iterable, value, start=0, stop=None):
982999 "Return indices where a value occurs in a sequence or iterable."
9831000 # iter_index('AABCADEAF', 'A') → 0 1 4 7
@@ -1004,10 +1021,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
10041021 while True:
10051022 yield function()
10061023
1007-
1008- The following recipes have a more mathematical flavor:
1009-
1010- .. testcode ::
1024+ # ==== Mathematical operations ====
10111025
10121026 def multinomial(*counts):
10131027 "Number of distinct arrangements of a multiset."
@@ -1026,9 +1040,11 @@ The following recipes have a more mathematical flavor:
10261040 # sum_of_squares([10, 20, 30]) → 1400
10271041 return sumprod(*tee(iterable))
10281042
1043+ # ==== Matrix operations ====
1044+
10291045 def reshape(matrix, columns):
10301046 "Reshape a 2-D matrix to have a given number of columns."
1031- # reshape([(0, 1), (2, 3), (4, 5)], 3) → (0, 1, 2), (3, 4, 5)
1047+ # reshape([(0, 1), (2, 3), (4, 5)], 3) → (0, 1, 2) (3, 4, 5)
10321048 return batched(chain.from_iterable(matrix), columns, strict=True)
10331049
10341050 def transpose(matrix):
@@ -1038,10 +1054,12 @@ The following recipes have a more mathematical flavor:
10381054
10391055 def matmul(m1, m2):
10401056 "Multiply two matrices."
1041- # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) → (49, 80), (41, 60)
1057+ # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) → (49, 80) (41, 60)
10421058 n = len(m2[0])
10431059 return batched(starmap(sumprod, product(m1, transpose(m2))), n)
10441060
1061+ # ==== Polynomial arithmetic ====
1062+
10451063 def convolve(signal, kernel):
10461064 """Discrete linear convolution of two iterables.
10471065 Equivalent to polynomial multiplication.
@@ -1096,6 +1114,8 @@ The following recipes have a more mathematical flavor:
10961114 powers = reversed(range(1, n))
10971115 return list(map(mul, coefficients, powers))
10981116
1117+ # ==== Number theory ====
1118+
10991119 def sieve(n):
11001120 "Primes less than n."
11011121 # sieve(30) → 2 3 5 7 11 13 17 19 23 29
@@ -1663,6 +1683,36 @@ The following recipes have a more mathematical flavor:
16631683 ['A', 'AB', 'ABC', 'ABCD', 'B', 'BC', 'BCD', 'C', 'CD', 'D']
16641684
16651685
1686+ >>> ' ' .join(map (' ' .join, derangements(' ABCD' )))
1687+ 'BADC BCDA BDAC CADB CDAB CDBA DABC DCAB DCBA'
1688+ >>> ' ' .join(map (' ' .join, derangements(' ABCD' , 3 )))
1689+ 'BAD BCA BCD BDA CAB CAD CDA CDB DAB DCA DCB'
1690+ >>> ' ' .join(map (' ' .join, derangements(' ABCD' , 2 )))
1691+ 'BA BC BD CA CD DA DC'
1692+ >>> ' ' .join(map (' ' .join, derangements(' ABCD' , 1 )))
1693+ 'B C D'
1694+ >>> ' ' .join(map (' ' .join, derangements(' ABCD' , 0 )))
1695+ ''
1696+ >>> # Compare number of derangements to https://oeis.org/A000166
1697+ >>> [len (list (derangements(range (n)))) for n in range (10 )]
1698+ [1, 0, 1, 2, 9, 44, 265, 1854, 14833, 133496]
1699+ >>> # Verify that identical objects are treated as unique by position
1700+ >>> identical = ' X'
1701+ >>> distinct = ' x'
1702+ >>> seq1 = (' A' , identical, ' B' , identical)
1703+ >>> result1 = ' ' .join(map (' ' .join, derangements(seq1)))
1704+ >>> result1
1705+ 'XAXB XBXA XXAB BAXX BXAX BXXA XAXB XBAX XBXA'
1706+ >>> seq2 = (' A' , identical, ' B' , distinct)
1707+ >>> result2 = ' ' .join(map (' ' .join, derangements(seq2)))
1708+ >>> result2
1709+ 'XAxB XBxA XxAB BAxX BxAX BxXA xAXB xBAX xBXA'
1710+ >>> result1 == result2
1711+ False
1712+ >>> result1.casefold() == result2.casefold()
1713+ True
1714+
1715+
16661716 >>> list (powerset([1 ,2 ,3 ]))
16671717 [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
16681718 >>> all (len (list (powerset(range (n)))) == 2 ** n for n in range (18 ))
0 commit comments