函数和闭包之闭包

本文深入探讨Scala函数字面量中的闭包概念,解释如何在函数内部引用外部作用域的变量,以及闭包在运行时的行为特性。通过示例展示了闭包如何捕获自由变量并在外部可见,同时解释了闭包与自由变量之间的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

到这里为止,所有函数字面量的例子仅使用了传入的参数。例如:(x:Int) => x > 0里,函数体x > 0用到的唯一变量x被定义为函数参数。然而也可以参考定义在其他地方的变量,如:

(x:Int) => x + more            //more是多少?

相对地,x变量是一个绑定变量(bound variable),因为它在函数的上下文中有明确意义(被定义为函数的唯一参数)。
如果你尝试独立使用这个函数字面量,上下文内没有任何more的定义,编译器会报错:

115253_DDra_168814.jpg

如果上下文内有more的定义,这个函数字面量就会工作正常,如例:

115438_qp1I_168814.jpg

依照这个函数字面量在运行时创建的函数值(对象)被称为闭包(closure)。闭包名称源自于通过“捕获”自由变量的绑定,从而对函数字面量执行的“关闭”行动不带自由变量的函数字面量,如:(x:Int) => x + 1,被称为封闭项(closed term),这里项(term)指的是一小部分源代码。因此依照这个函数字面量在运行时创建的函数值严格意义上来讲就不是闭包,因为(x:Int) => x + 1在编写的时候就已经封闭了。但任何带有自由变量函数字面量,如:(x:Int) => x + more,都是开放项(open term)。因此,任何以(x:Int) => x + more为模板在运行期创建的函数值将必须捕获对自由变量more的绑定。因此得到的函数值将包含指向捕获的more变量的索引。又由于函数值是关闭这个开放项(x:Int) => x + more行动的最终产物,因此被称为闭包

这个例子带来一个问题,如果more在闭包创建之后被改变了会发生什么事?

120133_brL7_168814.jpg

直觉上,scala的闭包捕获了变量本身,而不是变量指向的值。就像前面演示的例子,依照(x:Int) => x + more创建的闭包看到了闭包之外做出的对more的变化。反过来也同样,闭包对捕获变量作出的改变在闭包之外也可见。下面是个例子:

125914_ciyw_168814.jpg

例子用一个循环的方式计算List的累加和。变量sum处于函数字面量sum += _的外围,函数字面量把数累加到sum上。尽管这是一个在运行期改变sum的闭包,作为结果的累加值,-11,仍然在闭包之外可见。

如果闭包访问了某些在程序运行时有若干不同备份的变量,例如闭包使用了某个函数的本地变量,并且函数被调用很多次会怎样?每一次访问使用的是变量的哪个实例?如下的说法通用于所有情况:使用的实例是那个在闭包被创建的时候活跃的。如例:

133204_0fqi_168814.jpg

每次函数被调用时都会创建一个新闭包。每个闭包都会访问闭包创建时活跃的more变量。调用makeIncreaser(11)时,捕获值11当作more的绑定的闭包被创建并返回。相似地,调用makeIncreaser(23),捕获23当作more的闭包被返回。

more是makeIncreaser方法的入参,按说应该存在于栈中。在这样的情况下,scala编译器重新做了布局以使得捕获的参数存在于堆中,而不是栈中,因此可以保留在创建它的方法调用之外,这种重新布局的工作都是自动完成的


转载于:https://my.oschina.net/fhd/blog/276738

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值