Since this started, I've been using variants of the following in my own code, wrapping `accumulate`:

I quite like it! It handles everything that's come up pretty naturally. A difference from what was discussed before: I opposed having a `skipfirst` argument because I didn't want an optional argument that _only_ applied if another optional argument was specified. But `skipfirst` in the above applies regardless of whether `initial` is specified. It turned out to be useful in cases where the _effect_ of `initial` had been gotten instead via slamming the initial value into the first object returned by `iterable`, and then ignored later by a special first case to throw away the first result `accumulate` returned.

I'm writing this now because it turned out I used it eight times yesterday, which raised it above background noise level ;-)

The first use captures a sequence countless thousands of Python programmers have crafted in various ways by hand:

from itertools import accumulate, chain, cycle, islice

def accum(iterable, func=None, *, initial=None, skipfirst=False):

if initial is not None:

iterable = chain([initial], iterable)

if func is None:

iterable = accumulate(iterable)

else:

iterable = accumulate(iterable, func)

if skipfirst:

iterable = islice(iterable, 1, None)

return iterable

I quite like it! It handles everything that's come up pretty naturally. A difference from what was discussed before: I opposed having a `skipfirst` argument because I didn't want an optional argument that _only_ applied if another optional argument was specified. But `skipfirst` in the above applies regardless of whether `initial` is specified. It turned out to be useful in cases where the _effect_ of `initial` had been gotten instead via slamming the initial value into the first object returned by `iterable`, and then ignored later by a special first case to throw away the first result `accumulate` returned.

I'm writing this now because it turned out I used it eight times yesterday, which raised it above background noise level ;-)

The first use captures a sequence countless thousands of Python programmers have crafted in various ways by hand:

def firstfactor(n, limit=1000):

for p in chain([2, 3],

accum(cycle([2, 4]), initial=5)):

# 2, 3, and integers not divisible by 2 or 3

# 2, 3, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, ...

if p*p > n:

return n

if p > limit:

return 0

if n % p == 0:

return p

The same sequence can be gotten more obscurely via:

for p in chain([2, 3],

accum(cycle([4, 2]), initial=1, skipfirst=True)):

The remaining 7 uses were in transcribing's Lehman's elegant[1] optimization of Fermat's "difference of squares" factorization method:

above his code uses

large([48, 24, 24, 24], -24)

The latter needs to ignore the initial (-24) value (except to add it to the first delta: -24 + 48 = 24). That was more natural in context, due to the way he advanced the generated in a `while True:` loop built out of gotos ;-)

[1] https://math.boisestate.edu/~liljanab/Cryptology1Fall2013/LehmanFactoring.pdf

Historical gloss: Lehman surprisingly transformed Fermat's O(n) method into an O(cube_root(n)) method. I believe that was the first deterministic factoring method known beating trial division's O(sqrt(n)) time. Alas for Lehman, Pollard very soon after published his `rho` method, which runs in probabilistic O(sqrt(p)) time where p is n's smallest prime factor, so in worst-case probabilistic O(fourth_root(n)) time. That relegated Lehman's method to an academic curiosity, although it remains supremely effective if N is the product of two odd primes whose ratio is close to a fraction with "small" numerator and denominator.

def large(deltas, init):

for k in accum(cycle(deltas), initial=init):

...

# ints divisible by 30

large([30], 30)

# ints divisible by 24 but not by 30

large([24, 24, 24, 48], 24)

# ints divisible by 12 but not by 30 or 24

large([24, 48, 24, 24], 12)

# ints divisible by 18 but not by 30, 24, or 12

large([36, 72, 36, 36], 18)

# ints divisible by 6 but not by 30, 24, 12, or 18

large([36, 24, 12, 24, 12, 24, 36, 12], 6)

# ints divisible by 2 but not by 30, 24, 12, 18, or 6

large([2, 4], 2)

# ints not divisible by 30, 24, 12, 18, 6, or 2

large([2], 1)

Lehman built his sequence generator "by hand" in Algol. where the delta array (`c` in his code) is inherited from `large`'s enclosing scope, and `large()` is passed just the number of deltas and the initial value. A more literal transcription of his code would have used:

instead with the delta lists rotated and with a different initial value. For example, instead of

def large(deltas, init):

for k in accum(cycle(deltas), initial=init, skipfirst=True):

...

instead with the delta lists rotated and with a different initial value. For example, instead of

large([24, 24, 24, 48], 24)

above his code uses

large([48, 24, 24, 24], -24)

The latter needs to ignore the initial (-24) value (except to add it to the first delta: -24 + 48 = 24). That was more natural in context, due to the way he advanced the generated in a `while True:` loop built out of gotos ;-)

Historical gloss: Lehman surprisingly transformed Fermat's O(n) method into an O(cube_root(n)) method. I believe that was the first deterministic factoring method known beating trial division's O(sqrt(n)) time. Alas for Lehman, Pollard very soon after published his `rho` method, which runs in probabilistic O(sqrt(p)) time where p is n's smallest prime factor, so in worst-case probabilistic O(fourth_root(n)) time. That relegated Lehman's method to an academic curiosity, although it remains supremely effective if N is the product of two odd primes whose ratio is close to a fraction with "small" numerator and denominator.