12 高階函數

作爲值的函數

  • 變量中存放函數
  • 將方法變爲函數
import scala.math._
val num = 3.45
val fun = ceil _        //這裏的 _ 表示這個函數,而不是沒有傳參數
//或 val fun = ceil(_)
//fun: Double => Double = <function1>
println(fun(3.45))
  • ceil是math這個包對象的方法,如果有來自類的方法,將其變成函數的方式
scala> val f = (_:String).charAt(_:Int)
f: (String, Int) => Char = <function2>

scala> val f1:(String,Int)=>Char = _.charAt(_)
f1: (String, Int) => Char = <function2>

scala> val add : (Int,Int)=>Int = (x,y)=>x+y
add: (Int, Int) => Int = <function2>
  • 可以對函數調用傳遞(存放在變量中或作爲參數傳遞給另一個函數)
scala> fun(3.13)
res10: Double = 4.0

scala> Array(3.1,3.4,5.7).map(fun)
res11: Array[Double] = Array(4.0, 4.0, 6.0)

匿名函數

  • 函數不一定有名字,就像數字也不一定有名字
  • 以def定義的都是方法,不是函數
  • 結合第二章的內容
  • 函數名的後面有時候是冒號有時候是等號。如果右側顯式指明函數的類型就用冒號,如果不顯式指明函數的類型就用等號,函數類型可以通過自動推斷得到
scala> (x:Double) => 3*x
res12: Double => Double = <function1>
//或者加名字
scala> val triple=(x:Double) => 3*x
triple: Double => Double = <function1>

scala> val triple1:(Double)=>Double =  3*_
triple1: Double => Double = <function1>

scala> val triple1:(Double)=>Double = x => 3*x
triple1: Double => Double = <function1>
//使用map,map後面也可以跟大括號
scala> Array(3.1,3.4,5.7).map(triple1)
res13: Array[Double] = Array(9.3, 10.2, 17.1)
scala> Array(3.1,3.4,5.7).map{triple1}
res14: Array[Double] = Array(9.3, 10.2, 17.1)

帶函數參數的函數

  • 函數作爲參數
  • 高階函數:函數作爲參數或返回值
  • 如下示例
//函數作爲參數
def valueAtQuator(f:(Double)=>Double):Double = f(0.25)
//(f: Double => Double)Double
//函數作爲返回值
def mulBy(factor:Double):Double=>Double = {(x:Double)=>factor*x}
//Double => Double = <function1>

參數類型推斷

  • scala會自動類型推斷
  • 給出_的類型有助於將方法變爲函數
//手動指明類型
scala> valueAtQuator((x:Double) => 3*x)
res19: Double = 0.75
//scala自動推斷x的類型
scala> valueAtQuator((x) => 3*x)
res20: Double = 0.75
//只有一個參數的時候,略去x的括號
scala> valueAtQuator(x => 3*x)
res21: Double = 0.75
//如果x在=>右邊只出現了一次,可以用_代替
scala> valueAtQuator(3*_)
res22: Double = 0.75

scala> (_:String).substring(_:Int,_:Int)
res0: (String, Int, Int) => String = <function3>

一些有用的高階函數

  • 練習scala集合庫中的一些接受函數參數的方法
  • foreach 將函數應用到每個元素,不返回值
  • filter交出所有滿足條件的元素
  • reduceLeft
scala> (1 to 9).map("*"*_).foreach(println)
*
**
***
****
*****
******
*******
********
*********

scala> (1 to 9).filter( _ % 2 ==0)
res3: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8)

scala> (1 to 9).map(_.toString).reduceLeft( (x,y) => (s"f(${x},${y})"))
res8: String = f(f(f(f(f(f(f(f(1,2),3),4),5),6),7),8),9)

scala> (1 to 9).map(_.toString).reduceRight( (x,y) => (s"f(${x},${y})"))
res9: String = f(1,f(2,f(3,f(4,f(5,f(6,f(7,f(8,9))))))))

scala> Array(4,1,2,6).sortWith(_<_)
res11: Array[Int] = Array(1, 2, 4, 6)

閉包

  • 閉包由代碼和代碼用到的任何非局部變量定義構成
  • 下面的示例中,每一個函數都有自己的factor設置
def mulBy(factor:Double):Double=>Double = {(x:Double)=>factor*x}

scala> def triple = mulBy(3)
triple: Double => Double

scala> def hale = mulBy(0.5)
hale: Double => Double

SAM轉換

  • Single Abstract method 帶有單個抽象方法
  • scala函數到java SAM接口轉換隻對函數字面量有效

柯里化

  • 將原來接受兩個參數的函數變成新的接受一個參數的函數的過程,新的函數返回一個以原有第二個參數作爲參數的函數。
scala> val mul = (x:Int,y:Int)=>x*y
mul: (Int, Int) => Int = <function2>

scala> val mulOneATime =(x:Int)=>((y:Int)=>x*y)
mulOneATime: Int => (Int => Int) = <function1>

scala> mulOneATime(6)(7)
res12: Int = 42
//柯里化簡化版 方法
scala> def mulOneATime1 (x:Int)(y:Int)=x*y
mulOneATime1: (x: Int)(y: Int)Int
//柯里化函數版
scala> val mulOneATime2 = (x:Int) => (y:Int)=>x*y
mulOneATime2: Int => (Int => Int) = <function1>
  • 柯里化把某個函數參數單獨提出來,以提供更多用於類型推斷的信息
  • _.equalsIgnoreCase(_)是一個經過柯里化的參數的形式傳遞
scala> val a = Array("hello","world")
a: Array[String] = Array(hello, world)

scala> val b = Array("Hello","world")
b: Array[String] = Array(Hello, world)

scala> a.corresponds(b)(_.equalsIgnoreCase(_))
res13: Boolean = true
//corresponds源碼
  def corresponds[B](that: GenSeq[B])(p: (A,B) => Boolean): Boolean = {
    val i = this.iterator
    val j = that.iterator
    while (i.hasNext && j.hasNext)
      if (!p(i.next(), j.next()))
        return false

    !i.hasNext && !j.hasNext
  }
發佈了75 篇原創文章 · 獲贊 83 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章