for simpler lambdas
Just write the pure expression (the body of your anonymous function) using the variables x1
, x2
, ..., x9
(found in the module lambdax
) as parameters of your lambda, e.g.:
from lambdax import x, x1, x2, x3 paraboloid = (x1 / 3.) ** 2 + (x2 / 7.) ** 2 get_42nd = x[42] golden_root = x ** 2 - x - 1
x
is just a predefined alias for x1
to have prettier lambdas when taking a single parameter.
Note: you can also import and use X
and X1
, X2
, etc. (upper-case aliases) to avoid name conflicts,
if you already use the identifier x
or x1
, x2
etc. in your code.
To apply your lambda, just call it as a usual function, where all arguments are positional and must not be named.
First argument replaces all occurrences of x1
(or x
) in the expression, second one replaces x2
, etc.
Multivariate lambdas can be reduced by providing either all arguments "separately" or an only "iterable" of arguments.
from lambdax.test import assert_value # two arguments for two different placeholders: assert_value(paraboloid(10, 20), (10 / 3.) ** 2 + (20 / 7.) ** 2) # one parameter to replace the only x: assert_value(get_42nd({42: "the answer"}), "the answer") # one parameter to replace the two occurrences of the same x: assert_value(golden_root(1.618), 1.618 ** 2 - 1.618 - 1) # the first parameter goes to every x1, the second one for x2, the third one for x3: assert_value((x2 * x1 + x1 * x3)(.1, .2, .3), .2 * .1 + .1 * .3) # all arguments are provided as one list of values: assert_value((x1 + x2 * x3)([2, 3, 5]), 2 + 3 * 5)
An expression is an abstraction (a "lambda") if it starts with one of the public members of the modules
found in lambdax
, i.e. a magic variable, a provided operator-as-function, a builtin-as-lambda, the
special function λ
(see below), or with an arithmetic operation (in that case, at least one of the
operands must be an abstraction itself).
Useful to know: if you want to call a standard function as part of a lambda expression, for instance with an abstract parameter, you must first turn the function itself into an abstraction. Just wrap it with the function
λ
to do so. Example:from lambdax import λ def already_existing_function(a, exp=1): return a ** exp - 1 # the lambda below will take one parameter, call the standard function with two parameters # and modify the result: my_lambda = (λ(already_existing_function)(x, exp=x) - 5) * 2 assert_value(my_lambda(3), ((3 ** 3 - 1) - 5) * 2) # note: an only constant is irreducible (it would be pointless to reduce a constant...); # it allows to call a standard function inside an expression: my_int = λ(int)() # this is not the reduction of λ(int) but the equivalent of `lambda: int()` assert my_int is not int assert callable(my_int) # and because `my_int` is not an only constant (it's a call to a constant), it's reducible: assert_value(my_int(), 0)
If there is a call with parameter(s) applied on a variable expression inside your lambda, at least one of the parameters must be named or be an abstraction to distinguish your declaration from a β-reduction. You can apply
λ
on an argument if none of them already is a λ-abstraction. Examples:assert_value(x1(2), 2) # calling x1 with 2 as argument applies the identity function to 2 apply_is_back = x1(x2) # the call is clearly part of the abstraction on_4dot2 = x(λ(4.2)) # it's explicitly an abstraction thanks to `λ` imaginary_4_as = x(imag=4) # it's an abstraction: there is a named parameter in the call just_call = x() # it's an abstraction: the callee is variable but no parameter is provided
If the callee is a constant however, it won't be reduced, no matter the parameters provided (see 1.):
called = λ(already_existing_function)(3, 2) reduced = called() assert_value(reduced, 3 ** 2 - 1)
If you were wondering, the lambdas defined above can be used like that:
assert_value(apply_is_back(len, "abc"), 3) assert_value(on_4dot2(int), 4) assert_value(imaginary_4_as(complex), complex(0, 4)) assert_value(just_call(str), '')
The package re-implements the common "operator" functions provided by the built-in module
operator
to be directly usable in a lambda expression.Caution: the functions
and_
andor_
are functional equivalents for keywordsand
andor
, not for bitwise operators&
and|
despite what has been done in the built-in moduleoperator
. The goal here is to be consistent with the provided functionsnot_
,is_
andis_not
, which match the keyword operatorsnot
,is
andis not
. Plus there is no need for bitwise operators as functions, since they are all supported as double-underscore-methods inlambdax
.
Also, the ternary operator
if_
has been implemented. Its operands are evaluated lazily, just like those ofand_
andor_
.from lambdax import contains, and_, if_ assert contains([1, 2, 3], x)(2) is True assert contains(x, 4)([1, 2]) is False assert_value(and_(x, 6)(3), 6) assert_value((x & 6)(3), 2) assert_value(if_(x % 2, x * 3 + 1, x / 2)(3), 10)
All the relevant built-in functions are also redefined (though suffixed by
_λ
) as abstractions. You can also choose - it's not advised - to override the built-ins you want by just importing them fromlambdax.builtins_overridden
. Here, the built-ins that can be useful in a functional context are redefined such that they behave like abstractions when at least one of the given parameters is an abstraction, and like the original built-ins otherwise.from lambdax.builtins_as_lambdas import isinstance_λ from lambdax.builtins_overridden import isinstance as isinstance_mixed assert isinstance_λ(x, int)(2) is True assert isinstance_mixed(2, int) is True assert isinstance_mixed(x, int)(2) is True assert isinstance_mixed(2, x)(int) is True
You can compose λ-abstractions by explicitly calling one of the functions comp
, circle
or chaining
:
"g ∘ f" in mathematics is written in this context as comp(g, f)
, circle(g, f)
or chaining(f, g)
(mind the order of parameters).
Caution:
- g(f) is never a composition of f
and g
- if both f and g use the same variable X, they will share the same input in g(f)
. Just don't do that...
from lambdax.builtins_as_lambdas import dict_λ
complexes = [10 + 2j, 3 + 40j, 5 + 6j]
to_dicts = list(map(dict_λ(r=x.real, i=x.imag), complexes))
assert isinstance(to_dicts, list) and all(isinstance(v, dict) for v in to_dicts)
assert to_dicts == [{'r': 10, 'i': 2}, {'r': 3, 'i': 40}, {'r': 5, 'i': 6}]
imag_by_real = list(map(x['i'], sorted(to_dicts, key=x['r'])))
assert isinstance(imag_by_real, list) and all(isinstance(v, float) for v in imag_by_real)
assert imag_by_real == [40, 6, 2]
values = list(map((-x * 3) % 8, range(5)))
assert all(isinstance(v, int) for v in values)
assert values == [0, 5, 2, 7, 4]
assert list(map(x2 ** x1, enumerate([-1, 1] * 3))) == [1] * 6
# [(-1) ** 0, 1 ** 1, (-1) ** 2, ...]
Don't use this if you need performance, as it will give you lambdas that are about 20x slower
than the classic ones (using the keyword lambda
)! Run python -m lambdax.test.benchmark
to see it by yourself.