关于Scala的函数,我们之前的教程还有很多没有涉及,我们在这里一并讲一下。

部分应用

我们可以将一个方法通过后边加一个空格和_来转化为函数。如果你在一个需要函数的地方,传入一个方法名,Scala会隐式帮我们将方法转化为函数。我们可以看一下例子:

scala> def callAddFunc(addFunc:(Int,Int)=>Int,arg1:Int,arg2:Int)=addFunc(arg1,arg2)
callAddFunc: (addFunc: (Int, Int) => Int, arg1: Int, arg2: Int)Int

通过上边代码我们定义了一个callAddFunc的方法,它有三个参数,第一个参数是函数,签名是(Int,Int)=>Int,第二个参数是加法的第一个操作数arg1,第三个参数是加法的第二个操作数arg2. 然后我们再定义一个方法Add:

scala> def add(a:Int,b:Int)=a+b
add: (a: Int, b: Int)Int

我们可以通过下边两种方法将方法转化为函数:

scala> callAddFunc(add,1,2)
res13: Int = 3
scala> callAddFunc(add _,1,2)
res14: Int = 3

add _ 这个下划线在这里就是部分应用函数,因为我们没有给add传入足够的参数,所以是部分应用。同样你也可以只传一个参数:

scala> val addOne = add(1,_:Int)
addOne: Int => Int =
scala> addOne(3)
res16: Int = 4

我们给add只应用了第一个参数1,而第二个参数用_代替,这样我们就得到了一个给操作数加一的函数,函数签名是Int=>Int。

柯里化

我们可以这样重新定义add函数:

scala> def add(a:Int)(b:Int)=a+b
add: (a: Int)(b: Int)Int

这样我们就得到一个柯里化的函数,柯里化的函数可以这样调用:

scala> add(1)(2)
res31: Int = 3

也可以这样调用:

scala> val addOne = add(1)_
addOne: Int => Int =
scala> addOne(2)
res32: Int = 3

是的,你可能发现它和部分应用差不多。实际上它们的区别也不大。

传名参数

我们通过一个例子来看

scala> def foo(a:Int,b:Int)=if (a>0) a+b else a
foo: (a: Int, b: Int)Int

我们定义了一个函数,它有两个参数,如果a>0,则返回a+b,否则返回a。我们可以这样调用:

scala> foo(0,1+2)
res35: Int = 3

这个计算顺序是这样的,因为foo的第二个参数是1+2,它是一个表达式,在调用foo之前,会先计算1+2,并将结果3传入foo的第二个参数,这就是一般的传值调用。如果我们调用foo(0,2/0)就会得到一个除零的异常:

scala> foo(0,2/0)
java.lang.ArithmeticException: / by zero

其实在foo内部,因为a不大于0,会返回foo的第一个参数,根本用不到foo的第二个参数。但是因为是传值调用,却提前计算了并不需要计算的2/0. 我们可以将foo的第二个参数改为传名调用。传名参数的定义就是在参数名和参数类型之间加一个” =>”。

scala> def foo(a:Int,b: =>Int)=if(a>0) a+b else a
foo: (a: Int, b: => Int)Int
scala> foo(0,2/0)
res37: Int = 0

因为传名调用,所以2/0只有到计算的时候才会被计算。所以foo(0,2/0)可以得到结果。如果你的函数内部有分支,某些参数在某些分支并不需要,可以考虑用传名参数。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

%d 博主赞过: