在scala中使用闭包

时间:2018-05-12 13:08:31

标签: scala

我是斯卡拉的新手。我正在阅读关于闭包的内容,而不是我遇到的闭包定义:

  

“该名称来自于通过”捕获“其自由变量的绑定来”关闭“函数文字的行为”。

这条线究竟意味着什么?封闭真的有用吗?那是什么用例?

2 个答案:

答案 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 中计算自由变量的集合递归地如下(从叶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受内联函数定义的约束,因此来自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设置为值foreachforeach。因此,内部y => printf("%3d", x * y)使用x闭包,其中1设置为值9到{{1}}。< / p>