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 thiscurry()
.- 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'