Logo
Section 1 / 10

闭包与部分应用

学习如何使用闭包来创建函数的部分应用。

导入

闭包与部分应用

学习如何使用闭包来创建函数的部分应用。

这是两章内容中的第一章,将介绍函数式编程中的一些额外技术。

在本章中,我们将重新回顾闭包和组合,并探讨部分应用和柯里化。这些都是从现有函数创建新函数的方法。

在下一章中,我们将学习函子和单子。此外,我们还将了解 PyMonad 库提供的若干函数,它们可以帮你完成大量工作。

闭包

我们已经对闭包进行了较为深入的探讨,甚至专门用了一整章来讲解,因此这里仅作简要回顾。
当出现以下情况时,就会形成闭包:

  • 一个函数包含一个内部函数。
  • 外层函数返回这个内部函数。
  • 内部函数引用了外层函数作用域中的某些变量。

返回的内部函数被称为闭包。即使外层函数本身已经不再运行,该闭包仍然可以访问外层函数作用域中定义的值。

部分应用

部分应用是一种基于已有函数创建新函数的方式,但预先填入部分参数的值。

例如,下面是一个使用闭包实现 max 函数的部分应用:

1def maxn(n): 2 def f(x): 3 return max(n, x) 4 return f

现在,如果我们调用 maxn(0),它会返回一个内部函数 f(x) 的闭包,其中 n 的值被固定为 0。换句话说,它返回的是一个计算 max(0, x) 的函数。如下所示:

1def maxn(n): 2 def f(x): 3 return max(n, x) 4 return f 5 6max0 = maxn(0) 7print(max0(3)) # 等价于 max(0, 3) -> 3 8print(max0(-1)) # 等价于 max(0, -1) -> 0

我们成功创建了一个新函数 max0,并通过测试案例验证了它确实能正确计算 max(0, x)。实际上,它将所有负数替换为 0。

下面是一个结合 map 使用部分应用的例子。这里我们用 maxn 创建一个匿名函数,并将其用于 map 调用:

1def maxn(n): 2 def f(x): 3 return max(n, x) 4 return f 5 6m = map(maxn(3), [1, 2, 3, 4, 5]) 7print(list(m))

结果是 [3, 3, 3, 4, 5] —— 所有小于 3 的值都被限制为 3。一旦定义了 maxn 函数,就可以以简洁而清晰的方式实现 max 函数的部分应用。

涉及多个变量的函数

接下来我们看一个包含更多变量的函数。我们将使用一个计算二次函数的函数:

y=ax2+bx+cy = ax^2 + bx + c,其中 a,b,c,xa, b, c, x 为给定值。

下面是实现该计算的函数:

1def quad(a, b, c, x): 2 return a*x*x + b*x + c

通常情况下,a, b, c 是固定的,我们只希望改变 x 的值。这可以通过闭包轻松实现,如下所示:

1def quad_abc(a, b, c): 2 def f(x): 3 return quad(a, b, c, x) 4 return f

这使得我们可以创建一个用于计算特定二次函数的部分函数。例如:

x2−3x+2x^2 - 3x + 2

以下是创建该具体二次函数 myquad 的代码。你可以验证其计算结果是否正确(已知当 x=0x=0 时结果应为 2,且在 x=1x=1 和 x=2x=2 处有根):

1def quad(a, b, c, x): 2 return a*x*x + b*x + c 3 4def quad_abc(a, b, c): 5 def f(x): 6 return quad(a, b, c, x) 7 return f 8 9myquad = quad_abc(1, -3, 2) 10 11print(myquad(0)) # 2 12print(myquad(1)) # 0 13print(myquad(2)) # 0

我们也可以很容易地创建另一种部分应用;比如,假设我们想固定 a 和 c 的值,而让 b 和 x 作为后续调用时再指定的参数。

1def quad(a, b, c, x): 2 return a*x*x + b*x + c 3 4def quad_ac(a, c): 5 def f(b, x): 6 return quad(a, b, c, x) 7 return f 8 9myquad = quad_ac(1, 2) 10 11print(myquad(0, 1)) 12print(myquad(1, 2)) 13print(myquad(2, -1))

此时,myquad 是一个 a=1、c=2 的函数。调用 myquad 时需传入两个参数,分别对应 b 和 x 的值。

显然,通过创建不同的闭包,我们可以灵活选择任意组合的 a, b, c, x 进行预设,其余参数则在实际调用函数时再提供。

生成器

使用 functools.partial 进行部分应用