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
operatorto be directly usable in a lambda expression.Caution: the functions
and_andor_are functional equivalents for keywordsandandor, 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,isandis 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.