diff --git a/docs/api.rst b/docs/api.rst index 8079b8cf..724c39ac 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -211,12 +211,13 @@ Others .. autofunction:: numeric_range(start, stop, step) .. autofunction:: side_effect .. autofunction:: iterate +.. autofunction:: difference(iterable, func=operator.sub) ---- **Itertools recipes** .. autofunction:: consume -.. autofunction:: accumulate +.. autofunction:: accumulate(iterable, func=operator.add) .. autofunction:: tabulate .. autofunction:: repeatfunc diff --git a/more_itertools/more.py b/more_itertools/more.py index b8535e02..4c110420 100644 --- a/more_itertools/more.py +++ b/more_itertools/more.py @@ -14,7 +14,7 @@ takewhile, tee ) -from operator import itemgetter, lt, gt +from operator import itemgetter, lt, gt, sub from sys import maxsize, version_info from six import binary_type, string_types, text_type @@ -32,6 +32,7 @@ 'consecutive_groups', 'consumer', 'count_cycle', + 'difference', 'distinct_permutations', 'distribute', 'divide', @@ -1587,3 +1588,37 @@ def consecutive_groups(iterable, ordering=lambda x: x): enumerate(iterable), key=lambda x: x[0] - ordering(x[1]) ): yield map(itemgetter(1), g) + + +def difference(iterable, func=sub): + """By default, compute the first difference of *iterable* using + :func:`operator.sub`. + + >>> iterable = [0, 1, 3, 6, 10] + >>> list(difference(iterable)) + [0, 1, 2, 3, 4] + + This is the opposite of :func:`accumulate`'s default behavior: + + >>> from more_itertools import accumulate + >>> iterable = [0, 1, 2, 3, 4] + >>> list(accumulate(iterable)) + [0, 1, 3, 6, 10] + >>> list(difference(accumulate(iterable))) + [0, 1, 2, 3, 4] + + By default *func* is :func:`operator.sub`, but other functions can be + specified. They will be applied as follows:: + + A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ... + + For example, to do progressive division: + + >>> iterable = [1, 2, 6, 24, 120] # Factorial sequence + >>> func = lambda x, y: x // y + >>> list(difference(iterable, func)) + [1, 2, 3, 4, 5] + + """ + a, b = tee(iterable) + return chain([next(b)], map(lambda x: func(x[1], x[0]), zip(a, b)))