scala成长之路(7)函数进阶——可能是史上最浅显易懂的闭包教程
由于scala中函数内部能定义函数,且函数能作为函数的返回值,那么问题来了,当返回的函数使用了外层函数的局部变量时,会发生什么呢?没错,就产生是闭包。
关于闭包的解释网上一大堆,但基本上都是照葫芦画瓢,一个模子刻出来的,说来说去都只讲了“内部函数引用外层函数的局部变量”这个刻板的定义,根本没降到精髓。精髓是什么?且看我一句话给你讲清楚:
闭包就是外层函数函数的对象,外层函数就是闭包的构造函数!
怎么样,是不是摸不着头脑?其实是这样,我们使用闭包的目的并不是为了引用外层函数的局部变量,这只是手段,不是目的;我们的最终目的其实是函数的定制化,即按照一定的模板根据输出生成具有不同功能的函数,其实就是“函数的函数”。这里我们马上可以想到,就相当于“类”的思想,能根据不同的构造函数产生不同的“类的对象”。其实这里就是利用了这种思路,把外层函数当做构造函数,利用传入的参数进行定制,生成具有不同功能的函数并返回。
道理我都懂,但这跟“引用局部变量”有什么关系?很简单呀,比方说外层函数就是一个毛坯房,返回的函数时精装修房,那么外层函数的参数是装修图纸,最终得到的房子肯定是要用到这个图纸或者图纸的衍生物的,因此为了实现定制化,很定要依靠于外层函数的局部变量。
但是这里有个问题:我们都知道,正常来说函数的局部变量是存储在栈内存的,而对象是存储在堆内存的,因此同一个类可能存在多个对象,而函数在执行完成之后栈内存就会释放,因此不会存在多份函数的局部变量。但是根据我们上边的分析,定制化的函数肯定是要用到局部变量的,而且不同的定制化肯定是要保存多份局部变量,且相互之间空间独立的,就如同不同的对象一样。那么scala是怎么解决这个问题的呢?根据这篇文章 https://www.jianshu.com/p/8f24150fad2a 的介绍,scala在生成闭包时使用了一个临时对象来保存外层函数中的局部变量,因此可以算是将当前堆内存中的变量拷贝了一份到栈内存中,因此实现了多份局部空间的并存,以及函数闭包。
scala成长之路(7)函数进阶——可能是史上最浅显易懂的闭包教程的更多相关文章
- python成长之路七-函数的进阶
1,python中,名称空间分三种: 全局命名空间 局部命名空间(临时命名空间) 内置名称空间 2,作用域(两种): 1,全局作用域 包含:全局名称空间 内置名称空间 2,局部作用域 包含:局 ...
- scala成长之路(6)函数入门
众所周知,scala作为一门极客型的函数式编程语言,支持的特性包括: 函数拥有“一等公民”身份: 支持匿名函数(函数字面量) 支持高阶函数 支持闭包 部分应用函数 柯里化 首先需要指出,在scala中 ...
- scala成长之路(5)问题记录
还是在看scala sdk源码的时候,有很多问题要考自己慢慢摸索,这里做个记录. 一. 隐式转换的作用域? 隐式转换需要三个因素 1. 己方(当前对象) 2. 转换函数 3. 对方(转换的目标类) 这 ...
- scala成长之路(4)compaion object——伴生对象的使用
虽然java一直声称自己是完全面向对象的语言,但一直以来都被很多人所质疑,其中java的静态成员函数就是主要的“罪魁祸首”.由于java中保留了静态方法的调用,导致其编程模式依然有过程式编程的可能,尤 ...
- scala成长之路(3)隐式转换
不废话,先上例子:定义一个参数类型为String的函数: scala> def sayHello(name:String) = println("hello " + name ...
- scala成长之路(2)对象和类
scala提供了一种特殊的定义单例的方法:object关键字 scala> object Shabi{ | val age = 0 | val name = "shabi" ...
- scala成长之路(1)基本语法和数据类型
scala作为JVM上的Lisp,是一种geek类型的编程语言,也一直是我等java程序员眼中的梦寐以求的一门技能,遂下定决心花一段时间好好学习scala.第一天学习,主要介绍与java在编程上的主要 ...
- python成长之路六-函数的初识
定义函数 我们现学已知的python函数有<内置函数> 而我们现在要学的是<自定义函数> 1,def 定义一个函数 def name(): # 后接函数名 冒号 pass 2 ...
- Python之路----生成器函数进阶
def generator(): print(123) yield 1 print(456) yield 2 g = generator() ret = g.__next__() print('*** ...
随机推荐
- python进程与线程介绍
很多同学都听说过,现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统. 什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务.打个比方,你 ...
- win7下tomcat5.5无法通过ip和127.0.0.1访问的解决方法
解决办法:找到tomcat5.5目录下的conf\server.xml文件,原文如下: <Connector port="8080" maxHttpHeaderSize=&q ...
- SQL Server ->> CLR存储过程枚举目录文件并返回结果集
因工作需要写了个CLR存储过程枚举目录文件并返回结果集 using System; using System.IO; using System.Collections.Generic; using S ...
- 【NLP_Stanford课堂】句子切分
依照什么切分句子——标点符号 无歧义的:!?等 存在歧义的:. 英文中的.不止表示句号,也可能出现在句子中间,比如缩写Dr. 或者数字里的小数点4.3 解决方法:建立一个二元分类器: 检查“.” 判断 ...
- synchronized(this)、synchronized(class)与synchronized(Object)的区别
在多线程开发中,我们经常看到synchronized(this).synchronized(*.class)与synchronized(任意对象)这几种类型同步方法.但是是否知道这几种写法有什么区别了 ...
- Oracle里删除重复记录,保留一项
我们在使用数据库的时候,有时数据会有所重复,当我们只需要一项数据时,不需要显示重复的记录时 如下就有SQL代码: --查找表中多余的重复记录,重复记录是根据单个字段来判断 select * from ...
- GBase数据库——常用命令
针对GBase 8a 版本 1数据库操作与维护 1.1数据库启停 [root@OMMB-66-V10-001 ~]# service gcware stop Stopping GCMonit succ ...
- Laravel Service Provider 开发时设置延迟加载时遇到的问题
因实际项目需求,近日在开发 laravel-database-logger 包的时候,发现设置 ServiceProvider defer 属性设置为 true 时,会导致在register 方法中注 ...
- 笨办法学Python(三十九)
习题 39: 列表的操作 你已经学过了列表.在你学习“while 循环”的时候,你对列表进行过“追加(append)”操作,而且将列表的内容打印了出来.另外你应该还在加分习题里研究过 Python 文 ...
- 实验验证stack和heap中是否被设初值
#include <iostream> #include <stdlib.h> using namespace std; class Foo { public: int i; ...