首先要弄白闭包的概念。

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

维基百科中的说法是:在计算机科学中,闭包(英语: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. 手动爬虫之糗事百科(ptyhon3)

    一.调用封装的Url_ProxyHelper类,源码如下 import urllib.request as ur class Url_ProxyHelper: def __init__(self, u ...

  2. js处理事件冒泡

    给Dom元素添加事件时,有时候会遇到事件冒泡,处理方法如下: $("#Tab1 .close").live("click", function (event) ...

  3. IO流入门-第十一章-PrintStream_PrintWriter

    DataInputStream和DataOutputStream基本用法和方法示例 /* java.io.PrintStream:标准的输出流,默认打印到控制台,以字节方式 java.io.Print ...

  4. Hibernate 的查询

    1. Hibernate 框架的查询方式 唯一标识OID的检索方式: session.get(对象.class, OID) 对象导航的方式; HQL 检索方式; QBC 检索方式; SQL 检索方式 ...

  5. 【sed / awk脚本编写】

    awk awk分为BEGIN部分,正则匹配部分,END部分三部分. 我一般在BEGIN部分定义一些变量,正则部分用于匹配和执行一些解析和统计,END部分用于输出结果. 总体结构: awk 'BEGIN ...

  6. django之单表操作

    1.查询方法: <1> all(): 查询所有结果 <2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 <3> get(**kwargs ...

  7. hadoop namenode

    存储文件系统元数据,例如:文件目录结构,不同文件的分块情况,每块存储在那个节点,权限等 这些元数据全部存储在内存中,所以,namenode要求内存比较大 hdfs在存文件的时候会按照块存储,每一块默认 ...

  8. PAGELATCH_x和PAGEIOLATCH_x介绍

    Microsoft SQL Server企业级平台管理实践 第11章 Buffer Latch Timeout的解析 什么是PAGELATCH和PAGEIOLATCH 1.PAGELATCH_x和PA ...

  9. node.js---sails项目开发(2)

    1.安装mongoDB,这里用brew安装 brew install mongodb 2. 启动数据库 mongod 3.再打开一个终端,连接数据库 mongo 4.启动成功后,接下来就是新建一个数据 ...

  10. BFC(Block Formatting Context)基础分析

    W3C官方对于BFC的描述只有3小段,强烈建议想理解BFC的朋友先去看看,链接见文末. 常见的文档流分为:定位流.浮动流.普通流3种.BFC是普通流中的一种. 本文提出3个问题并给出使用BFC来解决这 ...