Scala函数解析

来源:互联网 发布:网络攻防技术技能试卷 编辑:程序博客网 时间:2024/06/07 21:58

在Scala中,函数是一等公民。究竟什么是函数?
数学上我们经常这样来定义函数 :

y = f(x)

其中,f为函数名称,x为输入,y为输出。x输入函数f之后,经过逻辑运算得到结果y。

函数定义

  def addOne(m: Int): Int = m + 1  val three = addOne(2)

如果函数没有参数,调用的时候可以省略括号:

  def three() = 1 + 2                             //> three: ()Int  three()                                         //> res0: Int = 3  three                                           //> res1: Int = 3

如果函数的逻辑比较复杂,可以使用{}括起来:

def timesTwo(i: Int): Int = {  println("hello world")  i * 2}

匿名函数:

 (x: Int) => x + 1                     //> res0: Int => Int = <function1>

可以将匿名函数付给某个val:

 val addOne = (x: Int) => x + 1        //> addOne  : Int => Int = <function1> addOne(1)                             //> res0: Int = 2

匿名函数经常作为其他函数的参数,在调用的时候传给其他函数,例如filter,map。

匿名方法也一样可以用{}括起来:

{ i: Int =>  println("hello world")  i * 2}

函数的本质

在scala中,函数(function)实际上是Trait的一个实例。

a function that takes one argument is an instance of a Function1 trait

如果函数有一个输入参数,则这个函数是Function1这个Trait的一个实例。通过scala的apply魔力,使得我们在调用对象时感觉像是在调用函数。例如我们定义一个加一函数:

object addOne extends Function1[Int , Int] {    def apply( old : Int) = old +1  }

根据apply的规则,当我们调用addOne(1)的时候,等同与将1传入到addOne对象的apply方法,因此经过运算后得到2:

addOne(1)                                 //> res0: Int = 2

如果通过class来实现:

class addOne extends Function1[Int , Int] {  def apply( old : Int):Int = old +1 }
val addOne = new addOne()         //> addOne  : com.scala.scala_tutorial.addOne = <function1>addOne(1)                         //> res0: Int = 2

函数就是通过这种方式来实现的,apply这个语法糖使得原来面向对象的东西看起来像函数,但是本质上,函数仍然只是Trait的一个实例。
scala中定义了Function1~Function22这个22个Trait,至于为什么是22,不得而知。在实现这些Trait的时候,通过 extends FunctionN是一种方式,但是可以使用更简单的形式:

class addOne extends (Int => Int){    def apply(old : Int): Int = old +1}

当我们定义匿名函数时:

 (x : Int) => x * 2                  //> res1: Int => Int = <function1>

(x : Int)实际上作为aply方法的参数,而右半边的则作为方法的实现。实际内部运行机制应该是这样的,首先有一个实现Function1的对象(class也可以):

object doubleFun extends Function1[Int , Int] {    def apply( x: Int) =x * 2}

如果我们将匿名函数赋给一个值:

val doubleFun = (x : Int) => x * 2   

此时这里的值就等同于doubleFun这个对象,调用的时候完全一致:

doubleFun( 2 )

总之,函数是对象。(Functions are objects)!

函数的Partial application

在数学中,如果一个函数有2个自变量(输入),当我们固定一个时,就只有一个自变量。

 z = f(x,y)=x+y z' = f(0,y)=y

类似地,在scala中,如果我们给一个多参数的函数中的某些参数赋值,其他部分不赋值(使用下划线),此时被部分赋值的函数就成为了另一个函数:

  def adder(m: Int, n: Int) = m + n               //> adder: (m: Int, n: Int)Int  val add2 = adder(2, _: Int)                     //> add2  : Int => Int = <function1>

我们可以选择函数中任意多个函数进行partial application。(通俗地说,就是固定任意多个自变量)

变长参数

类似于Java中使用…类表示方法的变长参数,scala使用号来表示变长参数,在正则表达式中表示出现0次或者任务多次。

def capitalizeAll(args: String*) = {    args.map { arg =>      arg.capitalize    }  }                                            capitalizeAll("hello")    //> res0: Seq[String] = ArrayBuffer(Hello)capitalizeAll("hello","world")    //> res1: Seq[String] = ArrayBuffer(Hello, World)

函数柯里化

柯里化,引用维基的定义:

是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术

对于下面的函数:

def multiply(m: Int)(n: Int): Int = m * n

我们可以直接调用:

multiply(3)(2)            //> res0: Int = 6

或者我们可以使用partial application,先固定某个参数,返回一个参数变少的函数,再调用该函数:

 val timesTwo = multiply(2) _         //> timesTwo  : Int => Int = <function1> timesTwo(3)                          //> res1: Int = 6

柯里化就是要把接收多个参数的函数,变成先接受一个参数,然后返回该参数固定后的函数:

  def multi(m:Int , n:Int) :Int = m * n   //> multi: (m: Int, n: Int)Int  val carriedMul =( multi _).curried      //> carriedMul  : Int => (Int => Int) = <function1>  val timesTwo = carriedMul(2)            //> timesTwo  : Int => Int = <function1>  timesTwo(3)                             //> res0: Int = 6

上面的例子中,multi函数原本接收两个参数m和n,通过柯里化(调用.curried)之后,变成了只接受一个参数的carriedMul函数,调用柯里化后的carriedMul,返回的是timesTwo函数。

柯里化使得我们可以先对函数应用一个参数,然后稍后再继续应用其他参数。

函数与方法

这两者经常不做区分,例如我们认为方法是可以访问类实例状态的函数。下面例子演示两者的微妙差别:

 class C {    var acc = 0    def minc = { acc += 1 }    val finc = { () => acc += 1 }  }  val c = new C                                   //> c  : test.C = test$C@43e47e37  c.minc             // 调用minc方法  c.finc             // 返回值为一个函数  > res0: () => Unit = <function0>
0 0