Skip to content

Itertool recipe improvements #109555

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 19, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 42 additions & 34 deletions Doc/library/itertools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,7 @@ which incur interpreter overhead.
return next(islice(iterable, n, None), default)

def quantify(iterable, pred=bool):
"Given a predicate that returns True or False, count the True results."
"Count how many times the predicate is True"
return sum(map(pred, iterable))

Expand Down Expand Up @@ -1028,34 +1029,6 @@ The following recipes have a more mathematical flavor:
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

def sieve(n):
"Primes less than n."
# sieve(30) --> 2 3 5 7 11 13 17 19 23 29
if n > 2:
yield 2
start = 3
data = bytearray((0, 1)) * (n // 2)
limit = math.isqrt(n) + 1
for p in iter_index(data, 1, start, limit):
yield from iter_index(data, 1, start, p*p)
data[p*p : n : p+p] = bytes(len(range(p*p, n, p+p)))
start = p*p
yield from iter_index(data, 1, start)

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
for prime in sieve(math.isqrt(n) + 1):
while not n % prime:
yield prime
n //= prime
if n == 1:
return
if n > 1:
yield n

def sum_of_squares(it):
"Add up the squares of the input values."
# sum_of_squares([10, 20, 30]) -> 1400
Expand All @@ -1073,14 +1046,21 @@ The following recipes have a more mathematical flavor:
return batched(starmap(math.sumprod, product(m1, transpose(m2))), n)

def convolve(signal, kernel):
"""Linear convolution of two iterables.
"""Discrete linear convolution of two iterables.

The kernel is fully consumed before the calculations begin.
The signal is consumed lazily and can be infinite.

Convolutions are mathematically commutative.
If the signal and kernel are swapped,
the output will be the same.

Article: https://betterexplained.com/articles/intuitive-convolution/
Video: https://www.youtube.com/watch?v=KuXjwB4LzSA
"""
# convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur)
# convolve(data, [1, -1]) --> 1st finite difference (1st derivative)
# convolve(data, [1, -2, 1]) --> 2nd finite difference (2nd derivative)
# 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))
Expand All @@ -1104,8 +1084,8 @@ The following recipes have a more mathematical flavor:
# Evaluate x³ -4x² -17x + 60 at x = 2.5
# polynomial_eval([1, -4, -17, 60], x=2.5) --> 8.125
n = len(coefficients)
if n == 0:
return x * 0 # coerce zero to the type of x
if not n:
return type(x)(0)
powers = map(pow, repeat(x), reversed(range(n)))
return math.sumprod(coefficients, powers)

Expand All @@ -1120,6 +1100,34 @@ The following recipes have a more mathematical flavor:
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
if n > 2:
yield 2
start = 3
data = bytearray((0, 1)) * (n // 2)
limit = math.isqrt(n) + 1
for p in iter_index(data, 1, start, limit):
yield from iter_index(data, 1, start, p*p)
data[p*p : n : p+p] = bytes(len(range(p*p, n, p+p)))
start = p*p
yield from iter_index(data, 1, start)

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
for prime in sieve(math.isqrt(n) + 1):
while not n % prime:
yield prime
n //= prime
if n == 1:
return
if n > 1:
yield n

def nth_combination(iterable, r, index):
"Equivalent to list(combinations(iterable, r))[index]"
pool = tuple(iterable)
Expand Down Expand Up @@ -1295,7 +1303,7 @@ The following recipes have a more mathematical flavor:
>>> polynomial_eval([], Fraction(2, 3))
Fraction(0, 1)
>>> polynomial_eval([], Decimal('1.75'))
Decimal('0.00')
Decimal('0')
>>> polynomial_eval([11], 7) == 11
True
>>> polynomial_eval([11, 2], 7) == 11 * 7 + 2
Expand Down