Functional Programming

calcpy provides APIs for argument reorganizing, currying, and execution.

Manage Arguments

calcpy provides APIs to manage arguments of callable functions. They can be used as decorators of functions.

calcpy.fun.cycleperm(cycle=())[source]

Callable that swaps position parameters according to cyclc notation.

Parameters:

cycle (list | tuple) – List of indices to swap.

Returns:

a callable that swaps a callable so that

its arguments are swapped according to cycle notation.

Return type:

Callable[callable, callable]

Examples

Permutate a function.

>>> permed = cycleperm(cycle=[0, 1])(range)
>>> permed(3, 2, 6)
range(2, 3, 6)
>>> permed = cycleperm(cycle=[1, 2])(range)
>>> permed(3, 2, 6)
range(3, 6, 2)

Use as a decorator.

>>> @cycleperm(cycle=[0, 1])
... def g(a, b):
...    return (a + b) * (a - b)
>>> g(2, 3)
5
calcpy.fun.swap(i=0, j=1)[source]

Callable that swaps positional arguments in a pair.

Parameters:
  • i (int) – Index of the argument to swap.

  • j (int) – Index of another argument to swap.

Returns:

a callable that swaps a callable so that two designated arguments are swapped.

Return type:

Callable[callable, callable]

Examples

Swap arguments of a callable:

>>> swapped = swap()(range)
>>> swapped(3, 2, 6)
range(2, 3, 6)
>>> swapped = swap(i=1, j=2)(range)
>>> swapped(3, 2, 6)
range(3, 6, 2)

Use as a decorator.

>>> @swap()
... def g(a, b):
...     return (a + b) * (a - b)
>>> g(2, 3)
5
calcpy.fun.dispatch(dispatcher=None, /, *, agg=None, fix_begin=0)[source]

Return callable that calculates using the first and each of the rest parameters with optional aggregation.

Parameters:
  • dispatcher (Callable[iterable, list[iterable]]) – Callable that accepts an iterable and returns a list of iterables. By default, the dispatcher is itertools.batched(*, n=1).

  • agg (callable, optional) – Aggretion.

  • fix_begin (int) – Number of parameters to fix.

Returns:

a callable that swaps a callable so that it is called repeated n times.

Return type:

Callable[callable, callable]

Examples

Apply an operator for each of positional parameter

>>> foreach = dispatch()
>>> dispatched = foreach(abs)
>>> list(dispatched(-2, 3, -4, 5))
[2, 3, 4, 5]

Apply an operator for each of positional parameter, and then multiple results all together

>>> from math import prod
>>> productionize = dispatch(agg=prod)
>>> dispatched = productionize(abs)
>>> dispatched(-2, 3, -4, 5)  # 2 * 3 * 4 * 5
120

Apply an operator for every two adjacent positional parameters, and then sum up

>>> from calcpy import pairwise  # from itertools import pairwise
>>> pairwise_sum = dispatch(pairwise, agg=sum)
>>> import operator
>>> dispatched = pairwise_sum(operator.mul)
>>> dispatched(-2, 3, -4, 5)  # -2 * 3 + 3 * -4 + -4 * 5
-38

Apply the first parameter to each of other positional parameter, and then sum up

>>> everyother_sum =  dispatch(agg=sum, fix_begin=1)
>>> dispatched = everyother_sum(operator.mul)
>>> dispatched(-2, 3, -4, 5)  # -2 * 3 + -2 * -4 + -2 * 5
-8

Extend an binary boolean operator to a multiple operator, return True only when all adjacent positional parameters return True.

>>> pairwise_all = dispatch(pairwise, agg=all)
>>> dispatched = pairwise_all(operator.lt)
>>> dispatched(-2, 3, -4, 5)
False

Use as a decorator.

>>> @dispatch(pairwise, agg=all)
... def fraceq(loper, roper):  # check whether the fractional parts are equal.
...     return (loper % 1) == (roper % 1)
>>> fraceq()   # no inputs, return True
True
>>> fraceq(0)   # one input only, return True
True
>>> fraceq(0.5, 1.5, 2.5)
True
>>> fraceq(0.1, 2.3)
False
calcpy.fun.pack(f)[source]

Merge all positional arguments of a function to a single tuple argument

Parameters:

f (callable)

Return type:

callable

Examples

Apply on a function.

>>> packed = pack(isinstance)
>>> packed([1.0, float])
True

Use as a decorator.

>>> import math
>>> @pack
... def g(*args):
...    return sum(arg + 1 for arg in args)
>>> g([1, 2, 3])
9
calcpy.fun.unpack(f)[source]

Replace a single tuple/list argument to many positional arguments.

Parameters:

f (callable)

Return type:

callable

Examples

Apply on a function.

>>> unpacked = unpack(all)
>>> unpacked(True, True, True)
True

Use as decorator.

>>> @unpack
... def g(values):
...    return max(values) - min(values)
>>> g(1, 2, 3)
2
calcpy.fun.prioritize(*index, dup='unique')[source]

Move some position parameters to the beginning.

Parameters:
  • index (int | list[int]) – Index of the elements to move to the beginning. The index can be negative.

  • dup ({"unique", "raise"}) – Specify how to deal with the case that the same positional argument is prioritized mutliple times. "unique": The same element will appear only once. "raise": Raise an error.

Return type:

Callable[callable, callable]

Examples

Use as a decorator. Move a single positional parameter to the front.

>>> @prioritize(1)
... def fun(a, b, c):
...     return [a, b, c]
>>> fun(1, 2, 3)
[2, 1, 3]

Use as a decorator. Move multiple positional parameters to the front.

>>> @prioritize(1, -1)  # has duplicated for this particular function
... def fun(a, b, c):
...     return [a, b, c]
>>> fun(1, 2, 3)
[2, 3, 1]

Use as a decorator. Drop duplicates.

>>> @prioritize(1, -2)
... def fun(a, b, c):
...     return [a, b, c]
>>> fun(1, 2, 3)
[2, 1, 3]
calcpy.curry(*args, **kwargs)[source]

Fill arguments of a callable.

If you want to fill positional arguments in the middle without filling argumetns in the begining, you can use prioritize() to move those positional parameter to the beginning, and then fill them using this curry().

Parameters:
  • args (tuple) – Positional arguments to fill.

  • kwargs (dict) – Keyword arguments to fill.

Return type:

Callable[callable, callable]

Examples

Use as a decorator:

>>> @curry(2, 3)
... def muladd(a, b, c):
...     return a * b + c
>>> muladd(4)
10

Use as a decorator, together with prioritize():

>>> from calcpy.fun import prioritize
>>> @curry(2, 3)
... @prioritize(-2, -1)
... def muladd(a, b, c):
...     return a * b + c
>>> muladd(4)
14
calcpy.extargs(mode=None)[source]

Enhance a function so that it can accept unused parameters.

Parameters:

mode (optional) – Method to resolve when both positional argument and keyword argument tries to write to the same parameter. The default value None means to raise when there are conflicts. inspect.Parameter.POSITIONAL_ONLY means to use the values in positional arguments. inspect.Parameter.KEYWORD_ONLY means to use the values in keyword arguments.

Returns:

Decorator that enhances the target function with parameter filtering.

Return type:

callable

Examples

Enrich a function so that it can accept additional parameters, overwriting default values.

>>> def print_fixed(a, /, b, c="c", *, d, e="e"):  # fixed parameters
...     print(f"a={a}, b={b}, c={c}, d={d}, e={e}")
>>> eprint_fixed = extargs()(print_fixed)
>>> eprint_fixed(0, 1, 2, 3, d="D", e="E", f="F")
a=0, b=1, c=2, d=D, e=E

Use default values.

>>> eprint_fixed(0, 1, d="D")
a=0, b=1, c=c, d=D, e=e

Raise due to missing obligatory positional arguments.

>>> eprint_fixed(c="C", d="D", e="E", f="F")
Traceback (most recent call last):
...
TypeError: missing a required argument: 'a'

Raise due to missing obligatory keyword arguments.

>>> eprint_fixed(0, 1)
Traceback (most recent call last):
...
TypeError: missing a required argument: 'd'

Raise due to the conflict between positional arguments and keyword arguments.

>>> eprint_fixed(0, 1, 2, 3, c="C", d="D", e="E", f="F")
Traceback (most recent call last):
...
TypeError: multiple values for argument 'c'

Use positional argument values when conflicting.

>>> import inspect
>>> pprint_fixed = extargs(inspect.Parameter.POSITIONAL_ONLY)(print_fixed)
>>> pprint_fixed(0, 1, 2, 3, c="C", d="D", e="E", f="F")
a=0, b=1, c=2, d=D, e=E

Use keyword argument values when conflicting.

>>> kprint_fixed = extargs(inspect.Parameter.KEYWORD_ONLY)(print_fixed)
>>> kprint_fixed(0, 1, 2, 3, c="C", d="D", e="E", f="F")
a=0, b=1, c=C, d=D, e=E

Deal with varied arguments.

>>> def print_var(a, /, b, c="c", *args, d, e="e", **kwargs):  # var parameters
...     print(f"a={a}, b={b}, c={c}, args={args}, d={d}, e={e}, kwargs={kwargs}")
>>> eprint_var = extargs()(print_var)
>>> eprint_var(0, 1, 2, 3, c="C", d="D", e="E", f="F")
Traceback (most recent call last):
...
TypeError: multiple values for argument 'c'
>>> pprint_var = extargs(inspect.Parameter.POSITIONAL_ONLY)(print_var)
>>> pprint_var(0, 1, 2, 3, c="C", d="D", e="E", f="F")
a=0, b=1, c=2, args=(3,), d=D, e=E, kwargs={'f': 'F'}
>>> kprint_var = extargs(inspect.Parameter.KEYWORD_ONLY)(print_var)
>>> kprint_var(0, 1, 2, 3, c="C", d="D", e="E", f="F")
a=0, b=1, c=C, args=(3,), d=D, e=E, kwargs={'f': 'F'}

Raise due to missing obligatory keyword arguments, when the original function has varied arguments.

>>> eprint_var(d="D")
Traceback (most recent call last):
...
TypeError: missing a required argument: 'a'

Function Execution

calcpy.call(f, *args, **kwargs)[source]

Call a callable with positional arguments and keyword arguments.

Parameters:
  • f – Callable object.

  • *args – Positional arguments.

  • **kwargs – Keyword arguments.

Returns:

Result of the callable.

Examples

>>> call(sum, [2, 3, 6])
11