Scala函数及函数式编程




Scala函数及函数式编程_控制结构


函数声明



Scala函数及函数式编程_控制结构_02


函数相当于Java中的静态方法:除了递归之外不需要声明返回类型

 

1. def as(x:Double)={if(x>=0) x else -x}  
2. def fac(n:Int):Int={if(n<=0) 1 else fac(n-1)}

 

   注意:一,函数声明中并没有声明变量是否可变,函数的参数默认声明是不可变,也就是val。

             二,不写没有返回值的方法必须用大括号括起来


简写


     1.除了递归函数以外,函数的返回类型都不用写,这是因为递归函数的时候只有在运行的时候才能知道返回类型是什么,这显然是不行的,scala只能在代码编译的时候做类型推测。

     2.当函数仅仅只有一条语句时不用加大括号


函数返回值


在scala中,语句块的最后一个值就是这个语句块的值,因此函数并不使用return来返回值,而是简单的把结果放到最后一条语句


函数使用简写


    1.如果一个方法仅仅有一个参数,那么就可以不用点号和括号来引用,使用空格分割即可,注意这种方法仅仅适用于类实例方法,并不能在函数中使用

    2.如果函数仅有一个参数,那么可以使用大括号来代替圆括号。如果使用柯里化,那么所有的函数都可以只有一个参数,这种情况下最后最后一个参数列表才可以这么做。 

      

1. def max(x:Any)(y:Any)=if(x>=y) x else y


1. max(1){2}//2

 


apply和update

apply:可以为一个类的伴生对象定义一个apply方法,相当于一个静态构造方法,定义在类的伴生对象中。可以让我们不加new来产生对象

1. var c = Array[Int](10)

也可以给类定义一个对象方法apply

1. var string = c(1)//可以看到把圆括号翻译成apply方法

 

update:可以为一个类定义一个update方法:f(arg1,arg2) = value,相当于f.update(arg1,arg2,value)

1. var score = Map()
1. score("Bob")=12


 


通用函数



Scala函数及函数式编程_控制结构_03


map


map接受一个处理单个元素的函数,map把这个函数运用在列表的每个元素上放回一个一样长的列表


filter


filter接受一个返回boolean的的值,filter会取出得到true的元素,返回过滤过的列表


foreach


类似于map


mkString


相当于toString,但是需要一个分隔符


zip


zip把两个列表的元素依次结合得到一个元祖集合


reduce


reduceLeft接受一个接受两个参数的函数,reduceLeft会依次把前一次得到的结果和下一个列表元素传递给函数,最后得到一个单个元素



 


fold


foldLeft和foldRight接受一个需要两个参数的函数,并且依层调用如(1::2::3).foldLeft(1)(_*_)=6



以层次话的方式求值


一等公民



Scala函数及函数式编程_java_04


匿名函数



Scala函数及函数式编程_scala_05


 

1. increase = (x: Int) => {

1. println("We")

1. println("are")

1. println("here!")

1. x + 1

1. }





    1. increase = (x: Int) => x + 9999


     


    简写

    注意简写只能用在向其他函数传递匿名函数时,由于简写利用的是自动推测,因此在其他情况下,编译器没有足够的信息来推测。

    1.去掉类型声明

    (x,y)=>x+y

    2.当只有一个参数时可以去掉圆括号

    x=>x+100

    3.如果参数在表达式中仅仅出现一次,可以用下划线来表示,这时候就没必要再前面写明参数列表,因为编译器会把参数一次填入下划线的位置,注意有几个下划线就有几个参数

     

    1. list.reduce(_+_)//两个下划线表示有两个不同的参数

    1. list.forEach(print _)

     

    事实上参数推测的能力更大

     

    1. def filesMatching(query: String,matcher: (String, String) => Boolean) = {

    1. for (file <filesHere;

    1. if matcher(file.getName, query))

    1. yield file

    1. }

    1. filesMatching(query, _.endsWith(_))
    1. filesMatching(query, _.contains(_))

    1. filesMatching(query, _.matches(_))


    函数传递


     


    1. someNumbers.foreach((x: Int) => println(x))

     


    赋值


     

    1. val a = sum _  
    2. increase = (x: Int) => x + 9999

     


    偏函数


     


      1. val b = sum(1, _: Int, 3)  
      2. b(2)  
      3. //对于类的偏函数  
      4. val c = new Rational(1,_:Int)  
      5. val vb = c(2)  
      6. vb.toString


       


      闭包


      1.闭包会在每次外层函数调用的时候重新产生,就好像他们分别有两份一样,事实上可以在每次调用时把闭包函数所使用的变量绑定得到一个函数。

      2.闭包语法:scala中不允许返回函数名,但是可以返回偏函数.

       

      1. def makeIncreaser(more: Int) ={  
      2.         def other(x:Int)=x+more  
      3.         val addMore = other _  
      4.         addMore  
      5.     }

       

      或者返回匿名函数

       

      1. def makeIncreaser(more: Int) ={  
      2.         val addMore = (x:Int)=>{x+more}  
      3.         addMore  
      4.     }

       


      可变参数

      注意scala只允许最后一个参数可变

      1. def echo(args: String*) =for (arg <args) println(arg)

       


      事实上args是一个Array,但是用这样的语法使得调用时,可以用多个参数调用



      echo("hello", "world!")



      但是不可以直接传递一个Array


      尾递归



      Scala函数及函数式编程_控制结构_06


      尾递归是指一个递归的递归语句出现在最后一个地方,注意不能有其他的语句,如下不可以。

      1. def boom(x: Int): Int =  
      2. if (x == 0) throw new Exception("boom!")  
      3. else boom(x-1)+ 1//最后的语句是一个加法


      原理


      尾递归在最后递归的地方并没有创建一个新的栈,而是跳转到函数的开头,因此可以大幅度的减小递归调用的开销。


      高阶函数



      Scala函数及函数式编程_java_07


      可以接受函数或者返回函数的函数叫做高阶函数。


      声明


       

      1. def filesMatching(query: String,matcher: (String, String) => Boolean) = {  
      2. for (file <- filesHere;if matcher(file.getName, query)) yield file  
      3. }


      这儿有一个概念叫做函数的类型,一个函数的类型就是:(参数列表类型)=>返回值类型。实际上Scala包里面定义了几个类来表示函数类型:Function1()


       


      柯里化

      把原来需要接受两个参数的函数变成两个接受一个参数的函数,新的函数接受一个参数并返回一个接受一个参数的函数

       

      1. def mul(x:Inr,y:Int)={x*y}

      1. def mul(x:Int)=(y:Int)=>x*y


       

       


      (仅仅scala支持的简写)





      1. def mul(x:Int)(y:Int)={x*y}


       


      实现控制结构



      Scala函数及函数式编程_控制结构_08


      1.在只有一个参数的函数中可以用大括号代替圆括号,因此函数更像控制结构

      2.控制结构简化后如下if(experssion*){experssion*},可以看做吧一个用两个参数的函数柯里化后的结果

       

      1. def withPrintWriter(file: File)(op: PrintWriter => Unit) {  
      2. new PrintWriter(file)  
      3. try {  
      4.          op(writer)  
      5. finally {  
      6.          writer.close()  
      7.     }  
      8. }  
      9. val file = new File("date.txt")  
      10. withPrintWriter(file) {  
      11. new java.util.Date)  
      12. }


       

      3.由于这种使用非常不方便,每次使用控制结构时都需要传递一个匿名函数,而且定义非常奇怪。因此scala引入了换名参数,在传统的控制结构中,后面的一部分可以看做一个无参有一个返回值的函数。声明如下:op:()=>Boolean,那么使用时就必须为()=>experssion。而换名参数把声明时的()换成了空格:op: =>Boolean,那么使用时就可以仅仅写expersion,这样就更加像传统的控制结构。同时,更重要的是,如果声明成函数。那么,由于声明和使用的地方不一致,也就不能在调用时使用本地变量。而换名参数正好克服了这种情况,因为他相当于在使用的地方声明,也就可以使用本地变量


      loan pattern


      在这种情况下,使用资源的函数不能控制资源,他只是声明需要一个资源,然后在产生资源的地方把资源借给他,使用之后,由产生资源的一方负责释放资源。因此资源从来没有赤裸裸地出现在程序中,保证了资源一定会得到释放。

       


      1. def withPrintWriter(file: File, op: PrintWriter => Unit) {  
      2. new PrintWriter(file)  
      3. try {  
      4.          op(writer)  
      5. finally {  
      6.          writer.close()  
      7.     }  
      8. }


       

      这儿的withPrintWrite把file资源借给op函数,op函数仅仅使用,没有管理资源的权限。


      模式匹配和样例类



      Scala函数及函数式编程_java_09


      样例类



      Scala函数及函数式编程_控制结构_10


      在类声明前面加上case关键字的叫做样例类


      特性


      1.每一个样例类都会有一个默认的apply方法



      2.样例类中主构造器中的属性都会产生字段。



      3.样例类添加了toString,hashcode和equal方法



      4.样例类还实现了一个copy方法,用于产生一个新对象



       val other = op.copy(operator="-",left=v,rigth=v)


      模式匹配



      Scala函数及函数式编程_java_11


       


      1. def simplifyTop(expr: Expr): Expr =   
      2.     expr match {  
      3. case UnOp("",UnOp("",e)) => e // Double negation  
      4. case BinOp("+", e, Number(0)) => e // Adding zero  
      5. case BinOp("*", e, Number(1)) => e // Multiplying by one  
      6. case _ => expr  
      7. }


       

      1.在这儿不仅仅有匹配还有变量的赋值,e就会在模式匹配的时候来得到变量的值

      2.匹配还允许进行嵌套。

      3._是通用匹配,其实就是这个结果一般都是无视或者抛出异常。


      能力



      Scala函数及函数式编程_java_12


      常量匹配


       

       

            
      
             
           
          
      1. def describe(x: Any) =   
      2. x match {  
      3. case 5 => "five"  
      4. case true => "truth"  
      5. case "hello" => "hi!"  
      6. case Nil => "the empty list"  
      7. case _ => "something else"  
      8.          }


      变量匹配


       

      1. expr match {  
      2. case 0 => "zero"  
      3. case somethingElse => "not zero: "+ somethingElse  
      4. }


       

      注意:

      1.scala仅允许每个变量在匹配中被赋值一次

       


      构造器匹配


       

       

      1. expr match {  
      2. case BinOp("+", e, Number(0)) => println("a deep match")  
      3. case _ =>  
      4. }


       

      这儿的类必须是样例类。


      集合匹配


      数组匹配

       

      1. arr match{  
      2. case Array(0) => "0"  
      3. case Array(x,y) => x+" "+y  
      4. case Array(0,_*) => "0..."  
      5. case _ => something else  
      6. }


       

      列表匹配

       


      1. lst match {  
      2. case 0 :: Nil => "0"  
      3. case x::y::Nil => x+" "+y  
      4. case 0::tail => "0..."  
      5. case _ => "something "  
      6. }


       

      元组匹配

       

       

      1. pair match{  
      2. case (0,_) => "0 ..."  
      3. case (y,0) => y + "0"  
      4. case _ => "neither is 0"  
      5. }


       


      类型匹配


       

       

      1. def generalSize(x: Any) = x match {  
      2. case s: String => s.length  
      3. case m: Map[_, _] => m.size  
      4. case _ => 1  
      5. }


      注意:

       

      1.使用类型匹配可以同时实现类型检测和类型转化,因此在scala中要使用模式匹配来

      2.由于java的类型擦除,故不能精确匹配泛型的类型


      模式守卫


       

       

       

      1. // match only positive integers  
      2. case n: Int if 0 < n => ...  
      3. // match only strings starting with the letter ‘a’  
      4. case s: String if s(0) == 'a' => ...


      密封类


       

       

      1. sealed abstract class Expr  
      2. case class Var(name: String) extends Expr  
      3. case class Number(num: Double) extends Expr  
      4. case class UnOp(operator: String, arg: Expr) extends Expr  
      5. case class BinOp(operator: String,left: Expr, right: Expr) extends Expr


       

      密封类仅允许同文件的类检测它,因此不用担心在模式匹配的时候出现未匹配的问题


      Option类

      其他用处



      Scala函数及函数式编程_java_13


      变量定义


       

      1. val exp = new BinOp("*", Number(5), Number(1))  
      2. val BinOp(op, left, right) = exp


       

      注意这儿的类必须是样例类