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('*** ...
随机推荐
- 【起航计划 036】2015 起航计划 Android APIDemo的魔鬼步伐 35 App->Service->Messenger Service Messenger实现进程间通信
前面LocalService 主要是提供同一Application中组件来使用,如果希望支持不同应用或进程使用Service.可以通过Messenger.使用Messgener可以用来支持进程间通信而 ...
- keras 多输出问题
转自:https://github.com/Xls1994/DeepLearningCode/blob/master/Keras/HedgeScope/multiOutputLSTM.py
- Service Broker完成实例之间的会话详细解读
首先了解service broker是什么东西: Service Broker 是数据库引擎的组成部分,因此管理这些应用程序就成为数据库日常管理的一部分. Service Broker 为 SQL S ...
- May 27th 2017 Week 21st Saturday
I learned the value of hard work by working hard. 只有真的努力了,才会知道努力的价值. I remember in the movie, The Da ...
- Jmeter入门2 http请求—简单的get请求
发送一个简单的get http请求 1 启动Jmeter,在测试计划上点击鼠标右键>添加>Threads(Users)>线程组 2 线程组界面.可设置线程数,几秒启动所有线程,循环次 ...
- 【洛谷5287】[HNOI2019] JOJO(主席树优化KMP)
点此看题面 大致题意: 每次往一个字符串末尾加上\(x\)个字符\(c\),或者回到某一历史版本,求\(KMP\)的\(\sum Next_i\). 问题转化 考虑到可以离线. 于是,我们就可以用一个 ...
- 2018.10.6 Hibernate配置文件详解-------ORM元数据配置 &&& hibernate主配置文件
ORM既然是实体与关系数据库的映射,那就需要建立实体和关系数据库之间的基础数据,也可以称为元数据.简单的说就是表示类与表.列与属性(get.set方法)等等之间对应关系的数据. Customer.hb ...
- CentOS 7.1上安装.Net Core
官方网站给出了几条命令: sudo yum install libunwind libicu curl -sSL -o dotnet.tar.gz https://go.microsoft.com/f ...
- 【洛谷P2324】[SCOI2005]骑士精神
骑士精神 题目链接 #include<iostream> #include<cstdio> using namespace std; int t,MAXD,sx,sy; ][] ...
- Android学习笔记_75_Andorid控件样式汇总
<!-- 设置activity为透明 --> <style name="translucent"> <item name="android: ...