让我也为不同的解决方法投入两分钱。
简单的单行 lambda 与普通函数有何不同?我只能想到缺少赋值、一些类似循环的构造(for、while)、try-except 子句……就是这样?我们甚至有一个三元运算符 - 很酷!所以,让我们尝试处理这些问题。
作业
这里的一些人正确地指出,我们应该看看 lisp 的 let 表单,它允许本地绑定。实际上,所有的非状态改变赋值只能用let来执行。但是每个 lisp 程序员都知道let 形式绝对等同于调用 lambda 函数!这意味着
(let ([x_ x] [y_ y])
(do-sth-with-x-&-y x_ y_))
与
相同
((lambda (x_ y_)
(do-sth-with-x-&-y x_ y_)) x y)
所以 lambdas 绰绰有余!每当我们想要进行新的分配时,我们只需添加另一个 lambda 并调用它。考虑这个例子:
def f(x):
y = f1(x)
z = f2(x, y)
return y,z
lambda 版本如下所示:
f = lambda x: (lambda y: (y, f2(x,y)))(f1(x))
如果您不喜欢在对数据执行操作后写入数据,您甚至可以创建 let 函数。你甚至可以咖喱它(只是为了更多的括号:))
let = curry(lambda args, f: f(*args))
f_lmb = lambda x: let((f1(x),), lambda y: (y, f2(x,y)))
# or:
f_lmb = lambda x: let((f1(x),))(lambda y: (y, f2(x,y)))
# even better alternative:
let = lambda *args: lambda f: f(*args)
f_lmb = lambda x: let(f1(x))(lambda y: (y, f2(x,y)))
到目前为止一切顺利。但是如果我们必须重新分配,即改变状态怎么办?好吧,我认为只要所讨论的任务不涉及循环,我们就可以在不改变状态的情况下绝对快乐地生活。
循环
虽然循环没有直接的 lambda 替代方案,但我相信我们可以编写非常通用的函数来满足我们的需求。看看这个斐波那契函数:
def fib(n):
k = 0
fib_k, fib_k_plus_1 = 0, 1
while k < n:
k += 1
fib_k_plus_1, fib_k = fib_k_plus_1 + fib_k, fib_k_plus_1
return fib_k
显然,就 lambdas 而言是不可能的。但是在编写了一个小而有用的函数之后,我们就完成了这个和类似的案例:
def loop(first_state, condition, state_changer):
state = first_state
while condition(*state):
state = state_changer(*state)
return state
fib_lmb = lambda n:\
loop(
(0,0,1),
lambda k, fib_k, fib_k_plus_1:\
k < n,
lambda k, fib_k, fib_k_plus_1:\
(k+1, fib_k_plus_1, fib_k_plus_1 + fib_k))[1]
当然,如果可能,应该始终考虑使用map、reduce 和其他高阶函数。
Try-except 和其他控制结构
似乎解决这类问题的一般方法是利用惰性求值,将代码块替换为不接受参数的 lambda:
def f(x):
try: return len(x)
except: return 0
# the same as:
def try_except_f(try_clause, except_clause):
try: return try_clause()
except: return except_clause()
f = lambda x: try_except_f(lambda: len(x), lambda: 0)
# f(-1) -> 0
# f([1,2,3]) -> 3
当然,这不是 try-except 子句的完整替代方案,但您始终可以使其更通用。顺便说一句,通过这种方法,您甚至可以使 if 表现得像函数一样!
总结:很自然地,提到的所有内容都感觉有点不自然,也不像 Python 那样漂亮。尽管如此 - 它的工作原理!并且没有任何evals 和其他技巧,所以所有的智能感知都可以工作。我也不是说你应该在任何地方使用它。大多数情况下,您最好定义一个普通函数。我只是表明没有什么是不可能的。