我是斯卡拉的新手。我正在阅读关于闭包的内容,而不是我遇到的闭包定义:
“该名称来自于通过”捕获“其自由变量的绑定来”关闭“函数文字的行为”。
这条线究竟意味着什么?封闭真的有用吗?那是什么用例?
答案 0 :(得分:3)
这是一个开放的术语:
def sumMore(x: Int): Int = x + more
因为more
显示为自由变量,即其值在sumMore
的主体内没有绑定。因此,你得到:
scala> def sumMore(x: Int): Int = x + more
<console>:7: error: not found: value more
def sumMore(x: Int): Int = x + more
^
通过为more
提供绑定,您可以使这个开放术语成为封闭的术语,即关闭行为。
相反,如果你有
val more: Int = 5
def sumMore(x: Int): Int = x + more
通过为sumMore
提供值,关闭 more
,您就会有一个闭包。
您可以在嵌套函数中看到此模式:
def fib(n: Int): Int = {
var num: Int = n
def innerFib(prev: Int, actual: Int): (Int, Int) = {
if (num == 0) (prev, prev)
else if (num == 1) (prev, actual)
else {
num -= 1
innerFib(actual, prev + actual)
}
}
innerFib(0, 1)._2
}
这里,innerFib
和变量num
是一个闭包,就像在fib
的主体范围内一样,num
正在为自由变量提供绑定出现在num
身体内的innerFib
。
答案 1 :(得分:2)
什么是自由变量?
考虑用于curried乘法的函数文字:
(x: Int) => (y: Int) => x * y
如果您只考虑子表达式 e:= (y: Int) => x * y
,那么您可以在 e 中计算自由变量的集合递归地如下(从叶x
和y
开始,然后从其部分构建表达式):
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
受内联函数定义的约束,因此来自y
的变量FREE(x * y)
不再是免费的。因此,变量y
绑定,而x
在表达式(y: Int) => x * y
中 free 。
什么是关闭?
现在,如果您将整个表达式(x: Int) => (y: Int) => x * y
并将其应用于某个常量7
,会发生什么?什么回来了?
返回的对象是表达式(y: Int) => x * y
的闭包,其中自由变量x
设置为值7
。所以,从某种意义上说,是
((x: Int) => (y: Int) => x * y)(7)
类似于具有单个方法的对象,看起来大致类似于
class y_to_x_times_y_closure(x: Int) {
def apply(y: Int) = x * y
}
实例化为y_to_x_times_y_closure(7)
。请注意,值7
不能驻留在某个函数的调用堆栈中,因为您可以轻松生成一大堆闭包,例如
for (i <- 0 to 1000) yield ((x: Int) => (y: Int) => x * y)(i))
必须在同一个线程中同时共存,并且i
的不同捕获值绑定到变量x
。因此,Scala中的闭包实现为堆驻留对象。
为什么这有用?
这实际上是一个太广泛的问题:它使您能够进行函数式编程。它在如此深层次的语言中构建,如果不在各地使用大量的闭包,你就无法做任何事情。
考虑以下简单示例:打印一位数字的乘法表:
for (x <- 1 to 9) {
for (y <- 1 to 9) {
printf("%3d", x * y)
}
println()
}
这会产生:
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
这些for
- 循环实际上被移植到Range
对象1 to 9
上的以下方法应用程序中:
(1 to 9) foreach { x =>
(1 to 9) foreach { y =>
printf("%3d", x * y)
}
println()
}
什么是y => printf("%3d", x * y)
- 传递给内部foreach
的东西?它实际上是一个带有自由变量x
的表达式。为了打印任何有意义的内容,它必须捕获x
的值,而1
的值又由外9
设置为值foreach
到foreach
。因此,内部y => printf("%3d", x * y)
使用x
的闭包,其中1
设置为值9
到{{1}}。< / p>