首先要弄白闭包的概念。

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

维基百科中的说法是:在计算机科学中,闭包(英语: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. 网络模型+三次握手+四次挥手+DNS+HTTPS

    网络模型+三次握手+四次挥手+DNS+HTTPS 这篇文章十分精华,所以整理一下: 一.网络模型 OSI七层模型,和TCP/IP五层模型(更为普遍) TCP/IP 协议集: 二.TCP协议(传输层)建 ...

  2. nodemailer发送邮件各个服务器接口

    来自:https://github.com/nodemailer/nodemailer-wellknown/blob/master/services.json#L125 {   "1und1 ...

  3. window下搭建python开发环境

    搭建一个python开发环境比较简单,所以就稍微记录一下. 1.下载python然后安装 2.配置环境变量 3.在eclipse添加PyDev插件 1.下载python 官网:https://www. ...

  4. python split(),os.path.split()和os.path.splitext()函数用法

    https://blog.csdn.net/T1243_3/article/details/80170006   # -*- coding:utf-8 -*- """ @ ...

  5. JAVA发送http GET/POST请求的两种方式+JAVA http 请求手动配置代理

    java发送http get请求,有两种方式. 第一种用URLConnection: public static String get(String url) throws IOException { ...

  6. rest_framework之url控制器详解

    一 自定义路由(原始方式) from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^books/ ...

  7. JQuery操作select中的option

    html页面代码例如以下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http ...

  8. unix 全缓冲、行缓冲、无缓冲

    基于流的操作最终会调用read或者write函数进行I/O操作.为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I/O库函数的次数. 基于流的I/O提供以下3种缓冲: 全 缓冲:直到缓 ...

  9. beego——模板函数

    beego 支持用户定义模板函数,但是必须在 beego.Run() 调用之前,设置如下: func hello(in string)(out string){ out = in + "wo ...

  10. go——结构

    Go语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型.结构体是由一系列具有相同类型或不同类型的数据构成的数据集合. 结构体定义需要使用type和struct语句.str ...