scala的一个最主要的特性就是支持函数编程。函数是函数编程中的一等公民:函数可以作为参数传递给其他函数,可以作为其他函数的返回值,甚至可以在其它函数中嵌套。这些高阶函数称为函数值。
举一个简单的例子:从1到某个数求和。使用Java很容易实现:
int sum(int max){ int result = 0; for (int i = 0; i <= max; i++) { result +=i; } return result; }
使用scala实现也没有多大区别。
现在再扩展下需求:对某个范围内的奇数或偶数求和。使用java该怎么办呢,最直接的方式分别为奇数和偶数求和写一套方案就行了。但是这样纯粹重复的体力工作好像有些招人烦。此时如果能把奇偶分析的逻辑作为参数传递进来肯定是极好的。看看是如何使用函数式的方案来进行实现的:
def totalOverRange(max: Int, func: Int => Int): Int = { var result = 0; for (i <- 1 to max) { result += func(i) } result }
这里定义了一个totalOverRange函数来进行计算,这个函数有两个参数max和func。其中max是一个普通参数,表示循环的最大值。func则是一个函数值,其本质上是一个函数,接收一个Int并返回一个Int。在totalOverRange的方法体里,循环值的逻辑处理交由函数值func来实现,totalOverRange只负责累计。
先来试一下从1到max的简单求和:
print(totalOverRange(5, i=>i))
上面的代码段在调用totalOverRange方法时传递了两个参数,第一个参数标识循环到5,第二个参数“i=>i”则是一个匿名函数(也就是只有实现没有名字的函数),用来表示对循环值的处理逻辑。在函数中使用“=>”将左侧的参数列表和函数实现分开。这里的函数实现只是简单地将参数i返回。需要注意的是这里不需要指定函数值参数的类型,scala会从totalOverRange方法中推断出参数的类型。看一下执行结果:
再来做一下对5以内的偶数求和:
print(totalOverRange(5, i => if (i % 2 == 0) i else 0))
一开始我是这样写函数值的:
i => if (i % 2 == 0) return i else return 0
添加了return,但是这样做是错误的。不明白为什么,希望稍后的学习中可以解惑。
看一下执行结果:
奇数运算的逻辑就不需要我写了。
使用函数值的一个好处在这里已经显示出来了:可以减少代码的重复。将公共代码放到一个函数里,将差异的部分封装为函数值参数传递进去,从而减少代码的重复。这样子看起来是有些类似于模板方法模式的,不过不需要定义那么多的接口和类而已。
再想一下,如果作为函数值的函数很复杂该怎么办呢?是使用函数方案还是模板模式方案?
我也不好说,具体情况再具体分析吧。
#######