[Python-ideas] Start argument for itertools.accumulate() [Was: Proposal: A Reduce-Map Comprehension and a "last" builtin]
Tim Peters
tim.peters at gmail.com
Tue Apr 10 12:58:48 EDT 2018
[Tim]
> Woo hoo! Another coincidence. I just happened to be playing with
> this problem today:
>
> You have a large list - xs - of N numbers. It's necessary to compute slice sums
>
> sum(xs[i:j])
>
> for a great many slices, 0 <= i <= j <= N.
Which brought to mind a different problem: we have a list of numbers,
`xs`. For each index position `i`, we want to know the largest sum
among all segments ending at xs[i], and the number of elements in a
maximal-sum slice ending at xs[i].
`accumulate()` is a natural way to approach this, for someone with a
functional language background. You'll have to trust me on that ;-)
But there are some twists:
- The identity element for max() is minus infinity, which accumulate()
can';t know.
- We want to generate a sequence of 2-tuples, despite that xs is a
sequence of numbers.
- In this case, we do _not_ want to see the special initial value.
For example, given the input xs
[-10, 3, -1, 7, -9, -7, -9, 7, 4]
we want to generate
(-10, 1), (3, 1), (2, 2), (9, 3), (0, 4), (-7, 1), (-9, 1), (7, 1), (11, 2)
Note: the largest sum across all non-empty slices is then
max(that_result)[0]. The code below could be easily changed to keep
track off that incrementally too, but this is already so different
from "plain old running sum" that I don't want more novelty than
necessary to make the points (a special initial value is needed, and
it's not necessarily insane to want to produce results of a different
type than the inputs).
The key part is the state updating function:
def update(state, x):
prevmax, count = state
newsum = prevmax + x
if newsum > x:
return newsum, count + 1
else:
return x, 1
That's all there is to it!
Then, e.g.,
>>> from itertools import accumulate, chain
>>> import math
>>> xs = [-10, 3, -1, 7, -9, -7, -9, 7, 4]
>>> initial = (-math.inf, 1)
>>> result = accumulate(chain([initial], xs), update)
>>> next(result) # discard unwanted value
(-inf, 1)
>>> list(result)
[(-10, 1), (3, 1), (2, 2), (9, 3), (0, 4), (-7, 1), (-9, 1), (7, 1), (11, 2)]
More information about the Python-ideas
mailing list