首先要弄白闭包的概念。

教材中的说法是:闭包是一种特殊的函数值,闭包中封闭或绑定了在另一个作用域或上下文中定义的变量。这里说闭包是一种特殊的函数值。

维基百科中的说法是:在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。运行时,一旦外部的函数被执行,一个闭包就形成了,闭包中包含了内部函数的代码,以及所需外部函数中的变量的引用。其中所引用的变量称作上值(upvalue)。这里说,闭包是引用了自由变量的函数,是函数。

到目前为止的scala程序例子里,用于函数值或代码块的变量和值都是绑定的。我们清楚的知道它们都绑定到哪儿,可以是局部变量或是参数。此外,还可以创建有未绑定变量的代码块。调用函数之前,必须绑定它们;不过,它们可以在局部范围和参数列表之外绑定变量。这就是称它们为闭包的原因。

开始正式的程序说明。

先来看这一段代码:

def loopThrough(number: Int)(closure: Int => Unit) {
for (i <- 1 to number) {
closure(i)
}
} var result = 0
val addIt = { value: Int => result += value } loopThrough(10) { addIt }
println("Total of values from 1 to 10 is " + result)
result = 0
loopThrough(5) { addIt }
println("Total of values from 1 to 5 is " + result)

上面的代码先创建了一个函数loopTotal()。在这个函数中,从1到第一个参数number之间的值都会参与到第二个参数函数值closure的运算中。

比较特殊的是addIt代表的函数值,在函数值addIt的代码块中,变量value被绑定到参数中,但是代码块中的result却不是在块中或参数列表中定义的。addIt中的result实际上是绑定在外部的变量result上。

随后的代码演示了如何使用闭包。在loopThrough中的每一次运算都会修改result的值。

需要注意的是,闭包中的绑定并不是获得闭包绑定变量值的一份副本,而是直接绑定到变量本身。因此如果将result的值重置为0,闭包也会看到这种变化。下面是另一个例子,闭包绑定到另一个变量product上:

var product = 1
loopThrough(6){product *= _}
println("Product of values from 1 to 5 is " + product)

在上面的代码里,_指向loopThrough()所传入的参数,product绑定到loopThrough()的调用方里叫这个名字的变量上。

看看三次调用loopThrough函数的结果:

这一节勉强算是学完了,但是对于闭包这个概念还是不甚了了。不清楚的地方有这么几点:

  • 闭包是什么,是方法?是函数?还是函数值?普通方法是不是也可以视为函数?
  • 闭包中是会有一个自由变量的,这个自由变量具体是可以在哪里定义?是在函数里定义?还是在类里定义的?
  • 如果闭包就是普通的方法或函数,变量也可以在类里定义,那java中的getter方法是不是也可以被叫做闭包?!显然是不可以的,但是为什么?
  • 目前比较可以确定的一点就是在scala中函数值如果引用了外部变量,那么函数值是可以被称为闭包的。但是闭包是不是就一定是函数值?
  • 在scala之外的其他语言,闭包是不是也可以当做函数值来使用?

#########

scala学习手记30 - 闭包的更多相关文章

  1. scala学习手记38 - 方法命名约定和for表达式

    方法命名约定 之前在学习<运算符重载>一节时曾经说过一个方法命名约定:方法的第一个字符决定了方法的优先级.现在再说另一个命名约定:如果方法以冒号(:)结尾,则调用目标是运算符后面的实例. ...

  2. scala学习手记23 - 函数值

    scala的一个最主要的特性就是支持函数编程.函数是函数编程中的一等公民:函数可以作为参数传递给其他函数,可以作为其他函数的返回值,甚至可以在其它函数中嵌套.这些高阶函数称为函数值. 举一个简单的例子 ...

  3. scala学习手记17 - 容器和类型推断

    关于scala的类型推断前面已经提到过多次.再来看一下下面这个例子: import java.util._ var list1: List[Int] = new ArrayList[Int] var ...

  4. scala学习手记8 - 自适应的默认做法

    scala有一些默认做法,会让代码更简洁.更易读写,下面列出了这样几个特性: 1. 支持脚本.scala支持脚本,因此无须将所有的代码都放到类里.如果脚本可以满足需求,就将代码放到一个脚本里,无须再创 ...

  5. Scala学习手记1 - 快速体验

    又重新开始了scala的学习,因为中断了太长时间,所以这次还得从零开始.学习的过程就记录在这个博客上了. 这次学习的教程是<scala程序设计 java虚拟机多核编程实战>,我在多看上买了 ...

  6. scala 学习笔记三 闭包

    闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量. 闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数. 如下面这段匿名的函数: val multiplier = (i: ...

  7. scala学习手记40 - 使用case类

    前面两节我们已经多次接触过case关键字了.case关键字不仅可以用在match/case中来执行模式匹配,也可以用来修饰类.不过用case修饰的类也主要是用来做模式匹配.在上一节曾经提到过match ...

  8. scala学习手记40 - case表达式里的模式变量和常量

    再来看一下之前的一段代码: def process(input: Any) { input match { case (a: Int, b: Int) => println("Proc ...

  9. scala学习手记39 - 模式匹配

    在java中有switch/case这样的模式匹配语句,可以匹配的类型包括int,byte,char,short, enum,在java8又支持了字符串. 在scala中也有类似的模式匹配语句,即ma ...

随机推荐

  1. 诡异的js

    [] + {}; 隐式转换后,是0 那 {} + []呢? var a = 42,b; b = ( a++, a);

  2. salt常用命令、模块、执行

    一.salt常用命令 salt 该命令执行salt的执行模块,通常在master端运行,也是我们最常用到的命令 salt [options] '<target>' <function ...

  3. finereport-JS

    JS实现定时刷新报表 setInterval("self.location.reload();",10000); //10000ms即每10s刷新一次页面. 注:对于cpt报表,若 ...

  4. Javascript调用WinForm方法

    window.external.MyMessageBox('javascript访问C#代码')

  5. 4.php奇葩的地方,反引号``

    今天我发现我从来没打过这外符号 ` 就是键盘的左上方, 1的左边不需要组合键, 直接按下即可.... 刚开始我还一直在找没找到.....百度一下.才知道

  6. 学习js的正确姿势

    轻松入门: http://dwz.cn/6yYLoo 全面理解: http://www.shouce.ren/api/view/a/1116 大师进阶: http://es6.ruanyifeng.c ...

  7. Jmeter Aggregate Report 与 Summary Report 分析

    关于Aggregate Report和 Summary Report里面每个字段的说明,在网上有很多资料,在此不做说明. 本文主要讲Aggregate Report与Summary Report对比我 ...

  8. Redis数据存储优化机制(转)

    原文:Redis学习笔记4--Redis数据存储优化机制 1.zipmap优化hash: 前面谈到将一个对象存储在hash类型中会占用更少的内存,并且可以更方便的存取整个对象.省内存的原因是新建一个h ...

  9. 关于session的常用用法

    (一)django有四中session实现方式 1.数据库(database-backed sessions) 2.缓存(cached sessions) 3.文件系统(file-based sess ...

  10. DRF(2) - 解析器,序列化组件使用(GET/POST接口设计)

    一.DRF - 解析器 1.解析器的引出 我们知道,浏览器可以向django服务器发送json格式的数据,此时,django不会帮我们进行解析,只是将发送的原数据保存在request.body中,只有 ...