Scala编程基础12:Scala函数3
1.Scala高阶函数
高阶函数(Higher-Order Function)就是操作其他函数的函数。Scala允许使用高阶函数,高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。以下实例中,apply()函数使用了另一个函数f和值v作为参数,而函数f又调用了参数v:
object Test1 {
def main(args:Array[String]){
println(apply(layout, 10));
}
def apply(f: Int => String, v: Int) = f(v);
def layout[A](x: A) = "[" + x.toString() + "]";
}
编译并执行以上代码,输出结果如下:
E:\Scala>scalac Test1.scala
E:\Scala>scala Test1
[10]
2.内嵌函数
Scala支持内嵌函数:即在一个函数内部定义另一个函数,定义在函数内部的函数称为局部函数。以下实例使用内嵌函数来实现阶乘运算:
object Test2 {
def main(args:Array[String]){
println(factorial(0));
println(factorial(1));
println(factorial(2));
println(factorial(3));
}
def factorial(i:Int):Int = {
def fact(i:Int, accumulator:Int):Int = {
if(i <= 1)
accumulator;
else
fact(i-1, i*accumulator);
}
fact(i,1);
}
}
编译并执行以上代码,输出结果如下:
E:\Scala>scalac Test2.scala
E:\Scala>scala Test2
1
1
2
6
3.Scala匿名函数
Scala中定义匿名函数的语法很简单,使用一个箭头”=>”,箭头左边是参数列表,箭头右边是函数体。使用匿名函数可以简化代码。下面的表达式定义了一个匿名函数,接受一个Int类型的输入参数x,函数体的功能是将x+1后返回,将该匿名函数赋值给一个变量inc:
var inc = (x:Int)=>x+1
上述定义的匿名函数,等价于下面的函数形式:
def add2 = new Function1[Int,Int]{
def apply(x:Int):Int = x+1;
}
以上实例的inc现在可以作为一个函数,使用方法如下:
var res = inc(99)
同样匿名函数可以有多个输入参数:
var mul = (x:Int,y:Int)=>x*y
现在mul可以作为一个函数,使用方式如下:
println(mul(3,4))
当然也可以没有输入参数:
var userDir = ()=>{System.getProperty("user.dir");}
现在userDir可以作为一个函数使用,使用方式如下:
println(userDir())
注意:前面介绍的Scala普通函数参数列表的特性同样适用于Scala匿名函数。
举个例子:
object Test3 {
def main(args:Array[String]){
println("multiplier(1) value = "+multiplier(1));
println("multiplier(2) value = "+multiplier(2));
}
var factor = 3;
var multiplier = (i:Int)=>i*factor;
}
编译并执行以上代码,输出结果如下:
E:\Scala>scalac Test3.scala
E:\Scala>scala Test3
multiplier(1) value = 3
multiplier(2) value = 6
4.Scala偏应用函数
Scala偏应用函数是一种表达式,不需要提供函数需要的所有的参数,只需要提供部分,或者不提供所需参数。如下实例,打印日志信息:
import java.util.Date;
object Test4 {
def main(args:Array[String]){
val date = new Date;
log(date,"message1");
Thread.sleep(1000);
log(date,"message2");
Thread.sleep(1000);
log(date,"message3");
}
def log(date:Date,message:String)={
println(date + "----" + message);
}
}
编译并执行以上代码,输出结果如下:
E:\Scala>scalac Test4.scala
E:\Scala>scala Test4
Tue May 15 21:40:16 CST 2018----message1
Tue May 15 21:40:16 CST 2018----message2
Tue May 15 21:40:16 CST 2018----message3
以上实例模拟了一个记录日志的程序,在实例中,log()方法接收两个参数:date和message。在程序执行过程中,调用了三次,参数date的值都相同,message值不同。我们可以使用偏应用函数简化以上方法,绑定第一个参数date,第二个参数使用下划线(_)替换缺失的参数列表,并把这个新的函数值的索引赋值给一个变量,修改后实例如下:
import java.util.Date;
object Test5 {
def main(args:Array[String]){
val date = new Date;
val logWithDateBound = log(date,_:String);
logWithDateBound("message1");
Thread.sleep(1000);
logWithDateBound("message2");
Thread.sleep(1000);
logWithDateBound("message3");
}
def log(date:Date,message:String)={
println(date + "----" + message);
}
}
编译并执行以上代码,输出结果如下:
E:\Scala>scalac Test5.scala
E:\Scala>scala Test5
Tue May 15 21:49:38 CST 2018----message1
Tue May 15 21:49:38 CST 2018----message2
Tue May 15 21:49:38 CST 2018----message3
以上实例相当于将原来两个参数的函数重新定义为一个参数的函数,简化了程序实现。
5.Scala函数柯里化(Currying)
柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。比较抽象,我们看一个实例来更好的理解这个过程:
正常情况下,定义一个函数的方式如下:
def add(x:Int,y:Int)=x+y
调用这个函数的方式如下:
add(1,2)
现在我们将这个函数变形一下:
def add(x:Int)(y:Int)=x+y
此时应该这样调用该函数:
add(1)(2)
等价于前一种调用方式,结果都是3.
像这样,将一个多维函数变形为一个一维函数链的过程,就叫做Scala的函数柯里化过程。
实现过程
add(1)(2)实际上是依次调用了两个普通函数(非柯里化函数),第一个调用使用一个参数x,返回一个函数类型的值,第二次使用参数y调用这个返回的函数,得到最终的计算结果。
实质上最先演变成这样一个方法:
def add(x:Int)=(y:Int)=>x+y
这个函数接收一个x为参数,返回一个匿名函数,该匿名函数又接收一个y参数,函数体为x+y。现在对这个方法进行调用:
val result = add(1)
这里的result是一个匿名函数:
(y:Int)=>1+y
所以为了得到最终的结果,继续调用result函数:
val sum = result(2)
最终打印出来的结果就是3.
下面是一个完整的实例:
object Test6 {
def main(args:Array[String]){
val str1:String = "Hello, ";
val str2:String = "Scala !";
println("str1 + str2 = "+strcat(str1)(str2));
}
def strcat(s1:String)(s2:String)={
s1+s2;
}
}
编译并执行以上代码,输出结果如下:
E:\Scala>scalac Test6.scala
E:\Scala>scala Test6
str1 + str2 = Hello, Scala !