我是斯卡拉新手。我在阅读有关闭包的内容时发现了闭包的定义:
“该名称源自通过“捕获”其自由变量的绑定来“关闭”函数文字的行为”。
这行代码实际上意味着什么?关闭到底有何帮助?它的用例是什么?
什么是自由变量?
考虑柯里化乘法的函数文字:
(x: Int) => (y: Int) => x * y
Run Code Online (Sandbox Code Playgroud)
如果仅考虑子表达式e := ,则可以按如下方式递归计算e中的自由变量(y: Int) => x * y集(从叶子和开始,然后从其部分构建表达式):xy
FREE(x) = {x}因为x是一个变量FREE(y) = {y}因为y是一个变量FREE(*) = {}因为*是一个内置常数,用于所有实际目的FREE(x * y) = FREE(*) U FREE(x) U FREE(y) = {} U {x} U {y} = {x, y}通过递归下降到子树FREE( (y: Int) => x * y ) = FREE(x * y) - {y} = {x, y} - {y} = {x},因为该变量y受内联函数定义的约束,因此变量yfromFREE(x * y)不再是自由的。因此变量在表达式中y是绑定的并且x是自由的(y: Int) => x * y。
什么是闭包?
现在,如果您采用整个表达式(x: Int) => (y: Int) => x * y并将其应用于某个常数(7例如 ),会发生什么?返回什么?
返回的对象是表达式的闭包(y: Int) => x * y,其中自由变量x设置为 value 7。所以,从某种意义上来说,结果是
((x: Int) => (y: Int) => x * y)(7)
Run Code Online (Sandbox Code Playgroud)
类似于具有单一方法的对象,看起来大致像
class y_to_x_times_y_closure(x: Int) {
def apply(y: Int) = x * y
}
Run Code Online (Sandbox Code Playgroud)
实例化为y_to_x_times_y_closure(7). 请注意,该值7不能驻留在某些函数的调用堆栈中,因为您可以轻松地生成一大堆闭包,例如
for (i <- 0 to 1000) yield ((x: Int) => (y: Int) => x * y)(i))
Run Code Online (Sandbox Code Playgroud)
必须在同一个线程中同时共存,并且不同的捕获值i绑定到变量x。因此,Scala 中的闭包被实现为堆驻留对象。
为什么这有用?
这实际上是一个太宽泛的问题:它使您能够进行函数式编程。它深深地内置于语言中,如果不到处使用大量的闭包,你就无法真正做任何事情。
考虑以下简单示例:打印一位数字的乘法表:
for (x <- 1 to 9) {
for (y <- 1 to 9) {
printf("%3d", x * y)
}
println()
}
Run Code Online (Sandbox Code Playgroud)
这会产生:
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81
Run Code Online (Sandbox Code Playgroud)
这些循环实际上被脱糖到对象for上的以下方法应用程序中:Range1 to 9
(1 to 9) foreach { x =>
(1 to 9) foreach { y =>
printf("%3d", x * y)
}
println()
}
Run Code Online (Sandbox Code Playgroud)
y => printf("%3d", x * y)传递给内部的 -thing是什么 foreach?它实际上是一个带有自由变量的表达式x。为了打印任何有意义的内容,它必须捕获 的值x,该值又由外部设置1为值。因此,内部与的闭包一起工作,其中被设置为值to 。9foreachforeachy => printf("%3d", x * y)x19
| 归档时间: |
|
| 查看次数: |
1323 次 |
| 最近记录: |