Scala學(xué)習(xí)筆記 A2/L1篇 - 高階函數(shù) Higher-Order Functions

教材:快學(xué)Scala

chapter 12. 高階函數(shù) Higher-Order Functions

12.1 functions as values

  • 方法(method)和函數(shù)(function)是不一樣的,method不是值,function是值
  • method用def定義 function可以賦值給一個(gè)val
  • 方法轉(zhuǎn)換成函數(shù): val func = 方法 _ Scala無法直接操作(manipulate)方法,只能直接操作函數(shù)
  • 函數(shù)的用法:1.調(diào)用 2.傳遞(到變量/到另一個(gè)函數(shù))

12.2 匿名函數(shù)

(x: Double) => 3 * x

scala> val triple = (x: Double) => 3*x // 定義函數(shù)
triple: Double => Double = <function1>

scala> def triple(x: Double) = 3*x // 定義方法
triple: (x: Double)Double
  • 函數(shù)參數(shù)可以不用圓括號()用花括號{}括住
    e.g.
    Array(3.14, 1.42, 2.0).map{ (x: Double) => 3 * x } 其中
    (x: Double) => 3 * x是函數(shù)map的參數(shù)??!

12.3 函數(shù)作為參數(shù)

  • 作為參數(shù)的函數(shù),它的類型寫作 (參數(shù)類型) => 結(jié)果類型
  • 帶函數(shù)參數(shù)的函數(shù)叫做高階函數(shù)(higher-order function)

12.4 參數(shù)類型推斷 Parameter Inference

  • 在必要信息給出的前提下Scala可自動(dòng)推斷參數(shù)屬于什么類型
def quarter(f: (Double) => Double) = f(0.25) // 類型為((Double) => Double) => Double
quarter((x: Double) => 3 * x)
quarter((x) => 3 * x) // 從傳入?yún)?shù)類型為(Double) => Double) 知道x的類型為Double 可省略
quarter(x => 3 * x) // 只有一個(gè)參數(shù)(x) 可寫成x
quarter(3 * _) // 如果x在=>右側(cè)只出現(xiàn)一次,可以用_代替x

12.5 一些有用的高階函數(shù)

  • 通用原則:如果你要的是一個(gè)序列的值,那么想辦法從一個(gè)簡單的序列轉(zhuǎn)化得出
    (1 to 9).map("*" * _).foreach(println _)
    (1 to 9).filter(_ % 2 == 0) // 2,4,6,8
    (1 to 9).reduceLeft(_ * _) // 從左到右reduce (..((1 * 2) * 3) * ... * 9)
    (1 to 9).foldLeft(100)(_ + _) // 145 與reduceLeft區(qū)別是提供了迭代初始值
    "Mary has a little lamb".split(" ").sortWith(_.length < _.length)
    等價(jià)于
    "Mary has a little lamb".split(" ").sortWith((a: String, b: String) => a.length < b.length)

12.6 閉包 Closures

  • A closure consists of code together with the definitions of any nonlocal variables that the code uses.
  • Java 8支持一種形式上受限的閉包

12.7 SAM轉(zhuǎn)換

  • 背景:Java不支持函數(shù)參數(shù),需要將動(dòng)作放進(jìn)一個(gè)實(shí)現(xiàn)某接口的類中,再傳遞類的實(shí)例作為參數(shù)
  • 這種接口通常只有單個(gè)抽象方法(Single Abstract Method) Java中被稱為SAM類型
  • 問題:如何實(shí)現(xiàn)函數(shù)參數(shù) -> SAM參數(shù)的轉(zhuǎn)換?
  • 答案:隱式轉(zhuǎn)換(implicit)語法,通過自定義隱式轉(zhuǎn)換實(shí)現(xiàn)需求
implicit def makeAction(action: (ActionEvent) => Unit) = // action: 函數(shù)參數(shù)
new ActionListener {                                     // ActionListener: 需要轉(zhuǎn)換成的實(shí)例
    override def actionPerformed(event: ActionEvent) { action(event) }  // actionPerformed: SAM
}
// 效果:在所有預(yù)期ActionListener對象的地方傳入任何(ActionEvent)=>Unit函數(shù)

12.8 柯里化 Currying

def mulOneAtATime(x: Int)(y: Int) = x * y

  • 用途:把多參數(shù)中的某個(gè)參數(shù)單獨(dú)拎出來,提供用于類型推斷的信息
val a = Array("hello", "world")
val b = Array("Hello", "World")
a.corresponds(b)(_.equalsIgnoreCase(_))

corresponds的定義為
def corresponds[B](that: Seq[B])(p: (A, B) => Boolean): Boolean
通過that參數(shù)知道B類型,用來推斷函數(shù)p的輸入類型

12.9 控制抽象 Control Abstractions

  • 函數(shù)是"頭等公民":一系列語句 -> 不帶參數(shù)也沒有返回值的函數(shù)
  • "不帶參數(shù)也沒有返回值的函數(shù)" 的函數(shù)類型:() => Unit
def runInThread(block: () => Unit) { // 輸入是一個(gè)過程(控制結(jié)構(gòu)),表現(xiàn)為一個(gè)"不帶參數(shù)也沒有返回值的函數(shù)"block
    new Thread {
        override def run() { block() }
    }.start()
}

// 調(diào)用:{}內(nèi)的是runInThread的參數(shù),一個(gè)無參數(shù)無返回的函數(shù)
runInThread { () => println("Hi"); Thread.sleep(1000); println("Bye") }

想在調(diào)用中省略()=>寫法,使用call-by-name(換名調(diào)用)

def runInThread(block: => Unit) { // call-by-name語法:參數(shù)名:=>類型
    new Thread {
        override def run() { block } // block不加()
    }.start()
}

// 調(diào)用:不用加()=>
runInThread { println("Hi"); Thread.sleep(1000); println("Bye") } 
  • call-by-name參數(shù):函數(shù)被調(diào)用的時(shí)候,參數(shù)表達(dá)式不會(huì)被求值,而在函數(shù)體內(nèi)每次出現(xiàn)都會(huì)求值一次
  • call-by-value參數(shù):就是普通的參數(shù),函數(shù)被調(diào)用的時(shí)候會(huì)被求值,然后函數(shù)體內(nèi)每次出現(xiàn)都不會(huì)重新計(jì)算
def until(condition: => Boolean)(block: => Unit) {
    if (!condition) {
        block
        until(condition)(block)
    }
}

var x = 10
until (x == 0) {
    x -= 1
    println(x)
}

12.10 return表達(dá)式

  • return在Scala的作用:函數(shù)A的函數(shù)體內(nèi)定義了匿名函數(shù)B,若B的函數(shù)體內(nèi)使用return語句,則直接跳出A
def indexOf(str: String, ch: Char): Int = {
    var i = 0
    until (i == str.length) {
        if (str(i) == ch) return i // 直接跳出indexOf
        i += 1
    }
    return -1
}

練習(xí)答案

  1. def values(fun: (Int) => Int, low: Int, high: Int) = (low to high) zip ((low to high).map(fun))
  2. reduceLeft(max(_,_))
  3. fibo(x: Int) = if(x != 0) (1 to x).reduceLeft(_*_) else 1
  4. fibo(x: Int) = (1 to x).foldLeft(1)(_*_)
  5. def largest(fun:(Int)=>Int, inputs:Seq[Int]) = inputs.map(fun).reduceLeft(max(_,_))
  6. def largestAt(fun:(Int)=>Int, inputs:Seq[Int]) = inputs.zip(inputs.map(fun)).reduceLeft((a, b) => if (a._2 > b._2) a else b )._1
def adjustToPair[A, B, C](fun: (A, B) => C) = (pair: (A, B)) => fun(pair._1, pair._2) 
(1 to 10).zip((11 to 20)).map(adjustToPair[Int, Int, Int](_+_))
  1. Array("i", "am", "cat").corresponds(Array(1, 2, 3))(_.length == _)
def myCorresponds[A, B](iThis: Seq[A], iThat: Seq[B], p: (A, B) => Boolean): Boolean = {
    val pairs = iThis zip iThat
    // pairs.map(adjustToPair[A, B, Boolean](p)).filter(_ == false).length == 0
    // 看完13.8以后學(xué)到的新姿勢
    pairs.map(adjustToPair[A, B, Boolean](p)).forall(_ == true)
}
  1. def unless(condition: => Boolean)(block: => Unit) = if (!(condition)) { block }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Scala與Java的關(guān)系 Scala與Java的關(guān)系是非常緊密的!! 因?yàn)镾cala是基于Java虛擬機(jī),也就是...
    燈火gg閱讀 3,608評論 1 24
  • FP 3.1.函數(shù) 函數(shù)的地位和一般的變量是同等的,可以作為函數(shù)的參數(shù),可以作為返回值。傳入函數(shù)的任何輸入是只讀的...
    時(shí)待吾閱讀 1,145評論 0 2
  • 在C語言中,五種基本數(shù)據(jù)類型存儲(chǔ)空間長度的排列順序是: A)char B)char=int<=float C)ch...
    夏天再來閱讀 4,061評論 0 2
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,867評論 0 10
  • 不管你是領(lǐng)導(dǎo),老板,老師,導(dǎo)師。我尊重你,你也得學(xué)會(huì)尊重我。三十多歲的副教授了,是老師沒教過你要講禮貌,還是家大人...
    csj007閱讀 526評論 0 0

友情鏈接更多精彩內(nèi)容