第6关:Scala函数柯里化和闭包
任务描述
本关任务:定义一个柯里化函数,求两个参数的最大公约数。
相关知识
为了完成本关任务,你需要掌握:
- 什么是柯里化;
- 什么是闭包。
函数柯里化
柯里化,俗称“部分求值”。一个柯里化函数首先是会接受一些参数,但是接受这些参数之后,该函数并不会立即求值,而是继续返回另一个函数,刚才传入的参数在函数形成的闭包中被保存起来。等到函数被真正需要求值的时候,之前传入的所有参数会被一次性用于求值。也就是说,柯里化是函数式编程的一种技巧,用于把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数。
实例:
首先我们定义一个函数:
def add(x:Int,y:Int)=x+y
那么我们应用的时候,应该是这样用:add(1,2)。
现在我们把这个函数变一下形:
def add(x:Int)(y:Int) = x + y
那么我们应用的时候,应该是这样用:add(1)(2),最后结果都一样是 3,这种方式(过程)就叫柯里化。
实现过程:
add(1)(2) 实际上是依次调用两个普通函数(非柯里化函数),第一次调用使用一个参数 x,返回一个函数类型的值,第二次使用参数 y 调用这个函数类型的值。
实质上最先演变成这样一个方法:
def add(x:Int)=(y:Int)=>x+y
那么这个函数是什么意思呢? 接收一个 x 为参数,返回一个匿名函数,该匿名函数的定义是:接收一个 Int 型参数 y,函数体为 x+y。现在我们来对这个方法进行调用。
val result = add(1)
返回一个result,那 result 的值应该是一个匿名函数:
(y:Int)=>1+y
所以为了得到结果,我们继续调用 result。
val sum = result(2)
最后打印出来的结果就是 3。
闭包
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。
如下面这段匿名的函数:
val multiplier = (i:Int) => i * 10
函数体内有一个变量 i,它作为函数的一个参数。如下面的另一段代码:
val multiplier = (i:Int) => i * factor
在 multiplier 中有两个变量:i 和 factor。其中的一个 i 是函数的形式参数,在 multiplier 函数被调用时,i 被赋予一个新的值。然而,factor 不是形式参数,而是自由变量,考虑下面代码:
var factor = 3
val multiplier = (i:Int) => i * factor
这里我们引入一个自由变量 factor,这个变量定义在函数外面。
这样定义的函数变量 multiplier 成为一个"闭包",因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
有意思的是,当这个自由变量发生变化时,Scala 的闭包能够捕获到这个变化,因此 Scala 的闭包捕获的是变量本身而不是当时变量的值。
同样的,如果变量在闭包在发生变化,也会反映到函数外面定义的闭包的值。比如:
可以看到在闭包中修改 sum 的值,其结果还是传递到闭包的外面。
编程要求
仔细阅读右侧编辑区内给出的代码框架及注释,在 Begin-End 间编写程序代码,计算两数的最大公约数。具体要求如下:
- 定义一个名为 gcd(a: Int)(b: Int) 的柯里化函数,返回值类型为 Int,计算 a、b 两数的最大公约数。
测试说明
平台将使用测试集运行你编写的程序代码,若全部的运行结果正确,则通关。
测试输入:
33
58
预期输出:
1
测试输入:
10
2
预期输出:
2
开始你的任务吧,祝你成功!
参考答案
import scala.io.StdIn
object Scala函数柯里化和闭包 {
// object Test {
/********** Begin *********/
def gcd(a: Int) (b: Int): Int =
if (b == 0) a
else gcd(b)( a % b)
/********** End *********/
def main(args: Array[String]): Unit = {
val a = StdIn.readInt()
val b = StdIn.readInt()
println(gcd(a)(b))
}
// }
}