I asked for assistance creating a Python Lambda function capable of assignments and multi-line lambdas in this post. Following @l0b0's suggestions, I realized that it was better constructed as a class.
There is a security risk associated with eval or exec and input strings as well as an increased difficulty debugging code but I wanted a more capable lambda for functional experiments. This is probably unsuitable for production code.
Print will provide a code string that can be executed if you add the arguments.
Any suggestions for further improvements would be appreciated.
class lambda_():
"""
Creates an executable anonymous function supporting multiple line and assignments.
For one line the form is λ("parameters : code incl assignment")(*arguments)
and the first exepression is assigned to rtrn which is returned
For multiline, use triple quotes with the parameters on the first line,
the : is followed directly by a line feed and then the code
Input:
code in the form "x, y,...: return_value_expression; other code"
Output is assigned to variable rtrn
"""
def __init__(self, code):
"""
self.parameters parses parameters (preceding the colon)
self.code is the code after the colon with extra
leading spaces removed from multiline lambdas
"""
self.parameters = []
val_idx = 0
val_name = []
i = 0
if ":" in code:
for i, c in enumerate(code): # loop until the : that ends parameters
if c in ",:":
if val_name:
self.parameters.append(''.join(val_name))
val_name = []
val_idx += 1
if c == ":":
break
elif c != " ":
val_name.append(c)
# self.code sets rtrn = None for multiline or sets rtrn = first expression for
# single line unless :: (used if first expression can't be evaluated)
if code[i+1] != "\n": # single line,
if code[i+1] != ":":
if code[i] != ":":
self.code = ''.join(("rtrn = ", code))
else:
self.code = ''.join(("rtrn = ", code[i+1:]))
else:
self.code = ''.join(("rtrn = None; ", code[i+2:]))
else: # multiline
# wont run if excess leading spaces so remove them
lead = 0
code_ = code[i+1:]
while code_[lead+1] == " ": # how many on 1st line?
lead += 1
for i in range(1, len(code_)): # remove that on all lines
if code_[i-1: i+1] == "\n ":
code_ = code_[: i] + code_[i+lead:]
self.code = "rtrn = None" + "\n" + code_ # executable code
def __call__(self, *args):
"""
All arguments are declared global as listcomps etc don't create closure
when called using exec, so first declare parameters & rtrn as globals,
then collect parameter=value pairs in assignments.
Multiline will return None unless you assign rtrn a value.
"""
define_globals = ', '.join(["global rtrn"] + self.parameters)
assign = '; '.join([' = '.join((p, str(args[i])))
for i, p in enumerate(self.parameters)])
exec('; '.join((define_globals, assign if assign else "pass", self.code)))
return rtrn
def __str__(self):
args = ["??" for _ in self.parameters]
define_globals = ', '.join(["global rtrn"] + self.parameters)
assign = '; '.join([' = '.join((p, str(args[i])))
for i, p in enumerate(self.parameters)])
return '; '.join((define_globals, assign if assign else "pass", self.code))
λ = lambda_ # rebind to linux ctrl+shift+u 03BB
Examples:
print(
λ("""var:
for i in range(var):
print(i, end = ",")
print(' ', end='')
if i == var - 1:
print(var)
print(var)
rtrn = [i for i in range(var, 0, -1)]
""")(5)
)
print(λ("x: [x*i for i in range(x)]; print('Final:', rtrn[-1])")(3))
print(λ("x, y:: from math import cos, sin; rtrn = cos(x) + sin(y)")(3, 5))
def applyToEach(L, f):
for i in range(len(L)):
L[i] = f(L[i])
testList = [1, -4, 8, -9]
apply_to_each(testList, λ("x: x if x >= 0 else -x"))
print(testList, "\n")
multiline = λ("""var:
for i in range(var):
print(i, end = ",")
print(' ', end='')
if i == var - 1:
print(var)
print(var)
rtrn = [i for i in range(var, 0, -1)]
""")
print("Multiline lambda") ; print(multiline)
p_lambda = λ('print("value")')
print("parameterless lambda") ; print(p_lambda)