scala学习手记30 - 闭包

时间:2022-09-09 08:45:06

首先要弄白闭包的概念。

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

*中的说法是:在计算机科学中,闭包(英语: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函数的结果:

scala学习手记30 - 闭包

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

  • 闭包是什么,是方法?是函数?还是函数值?普通方法是不是也可以视为函数?
  • 闭包中是会有一个*变量的,这个*变量具体是可以在哪里定义?是在函数里定义?还是在类里定义的?
  • 如果闭包就是普通的方法或函数,变量也可以在类里定义,那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. Linux命令学习-grep

    1.作用Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来.grep全称是Global Regular Expression Print,表示全局 ...

  2. 【svn】 linux svn 强制提交注释

    在svn版本库的hooks文件夹下面,复制模版pre-commit.tmpl cp pre-commit.tmpl pre-commit chmod +x pre-commit vi编辑,如下: #! ...

  3. hdu 2102 A计划

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=2102 A计划 Description 可怜的公主在一次次被魔王掳走一次次被骑士们救回来之后,而今,不幸 ...

  4. java&period;lang&period;IllegalArgumentException&colon; Requested window android&period;os&period;BinderProxy&commat;450b2f48 异常处理

    晕死的错误,改了半天也没想到是这样的原因,基础正要呀... 先看一下警告信息: 07-07 08:32:19.540: WARN/WindowManager(74): Failed looking u ...

  5. 装tortoiseSVN时遇到的坑

    今天给新电脑配置开发环境,线上SVN地址申请权限以后,在本地装了tortoiseSVN,可是安装了以后死活打不开.明明浏览器里可以打开SVN地址,但是tortoiseSVN的浏览器里却打不开,摆弄了很 ...

  6. SRM 440&lpar;1-250pt&comma; 1-500pt&rpar;

    DIV1 250pt 题意:小球从一段折线斜坡上滚下来,告诉所用时间,求重力加速度. 解法:二分答案模拟即可. tag:二分,simulation // BEGIN CUT HERE /* * Aut ...

  7. loadrunner 录制中文出现乱码的解决办法

  8. Java 容器 接口

    Java 中容器框架的内容可以分为三层: 接口(模型), 模板和具体实现. 在开发中使用容器正常的流程是,首先根据需求确定使用何种容器模型,然后选择一个符合性能要求的容器实现类或者自己实现一个容器类. ...

  9. mysql字段约束

    为了确保数据的完整性和唯⼀性,关系型数 据库通过约束机制来实现目. 一. unique 唯一性约束    : 值不可重复: 二. not null    非空约束    : 值不可为空: 三. def ...

  10. UNIX环境编程学习笔记(2)——文件I&sol;O之不带缓冲的 I&sol;O

    lienhua342014-08-25 1 文件描述符 对于内核而言,所有打开的文件都通过文件描述符引用.文件描述符是一个非负整数.当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符. ...