Scala - 快速学习08 - 函数式编程:高阶函数
生活随笔
收集整理的這篇文章主要介紹了
Scala - 快速学习08 - 函数式编程:高阶函数
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
函數式編程的崛起
函數式編程中的“值不可變性”避免了對公共的可變狀態進行同步訪問控制的復雜問題,能夠較好滿足分布式并行編程的需求,適應大數據時代的到來。函數是第一等公民
- 可以作為實參傳遞給另外一個函數
- 可以作為返回值
- 可以賦值給變量
- 可以存儲在數據結構里
函數類型和值
在函數式編程中,函數的使用方式和其他數據類型的使用方式完全一致,可以像任何其他數據類型一樣被傳遞和操作。 也就是說,可以像定義變量那樣去定義一個函數,函數也會有“值”,函數的“值”就是“函數字面量(Funciton Literal)”,也稱為函數文字量或函數常量。實際上,這個函數字面量其實就是一個匿名函數。 需要注意:- Scala語法要求函數的“值”采用“=>”而不是“=”。
- 在Scala中,函數類型的格式為 A => B,表示一個接受類型A的參數,并返回類型B的函數。
匿名函數
使用匿名函數(Anonymous Function),可以不需要給每個函數命名,大大簡化代碼編寫工作。 匿名函數的定義形式(也稱為“Lamda表達式”):(參數) => {表達式} 注意:如果參數只有一個,可省略參數的圓括號;如果表達式只有一行,可省略表達式的花括號。 val myNum: Int => Int = (x: Int) => { x * 2 } //> myNum : Int => Int = testrepl$$$Lambda$3/2093176254@799f7e29val myNum2 = (x: Int) => x * 2 //> myNum2 : Int => Int = testrepl$$$Lambda$9/804581391@c818063val myNum3: Int => Int = (x) => x * 2 //> myNum3 : Int => Int = testrepl$$$Lambda$10/1057941451@75bd9247myNum(3) //> res0: Int = 6myNum2(3) //> res1: Int = 6myNum3(3) //> res2: Int = 6def test1(x: Int): Int = { x * x } //> test1: (x: Int)Intdef test2(x: Int) = x * x //> test2: (x: Int)Int(x: Int) => x * x //> res0: Int => Int = testrepl$$$Lambda$8/431687835@5ba23b66val test3 = (x: Int) => x * x //> test3 : Int => Int = testrepl$$$Lambda$9/804581391@c818063val test4: Int => Int = (x) => x * x //> test4 : Int => Int = testrepl$$$Lambda$10/1057941451@75bd9247test1(3) //> res1: Int = 9test2(3) //> res2: Int = 9test3(3) //> res3: Int = 9test4(3) //> res4: Int = 9 示例說明:- 第1行:把匿名函數"(x: Int) => { x * 2 }"定義為一個值,賦值給myNum變量
- 第2行:省略myNum2的類型聲明“Int=>Int”,省略匿名函數的表達式花括號
- 第3行:省略x的類型聲明,省略匿名函數的表達式花括號
閉包
閉包是一個比較特殊的函數,反映了一個從開放到封閉的過程,返回值依賴于聲明在函數外部的一個或多個變量。 - 如果引用的變量是自由變量,沒有綁定具體的值,那么此時這個函數是“開放的”。
- 如果引用的自由變量被綁定具體的值后,不再是“自由變量”,從而構成一個封閉的函數,那么此時這個函數“被關閉”了。
高階函數
用函數作為形參或返回值的函數,稱為高階函數。 也就是說,高階函數就是一個接受其他函數作為參數或者返回一個函數的函數。 高階函數示例-1: def f(x: Int, y: Int) = x + y //> f: (x: Int, y: Int)Intdef operate(f: (Int, Int) => Int) = { f(4, 4) } //> operate: (f: (Int, Int) => Int)Intoperate(f) 示例說明:函數operate是一個接受函數參數的函數,因此是一個高階函數。 高階函數示例-2: //給定兩個數區間中的所有整數求和def sumInts(a: Int, b: Int): Int = {if (a > b) 0 else a + sumInts(a + 1, b)} //> sumInts: (a: Int, b: Int)IntsumInts(1, 5) //> res0: Int = 15//定義了一個新的函數sum,以函數f為參數def sum(f: Int => Int, a: Int, b: Int): Int = {if (a > b) 0 else f(a) + sum(f, a + 1, b)} //> sum: (f: Int => Int, a: Int, b: Int)Int//定義了一個新的函數self,該函數的輸入是一個整數x,然后直接輸出x自身def self(x: Int): Int = x //> self: (x: Int)Int//重新定義sumInts函數def sumInts2(a: Int, b: Int): Int = sum(self, a, b)//> sumInts2: (a: Int, b: Int)IntsumInts2(1, 5) //> res1: Int = 15 示例說明:函數sum的參數類型是(Int=>Int, Int, Int),結果類型是Int,也就是說函數sum是一個接受函數參數的高階函數。 高階函數示例-3: def sum(f: Int => Int, a: Int, b: Int): Int = {if (a > b) 0 else f(a) + sum(f, a + 1, b)} //> sum: (f: Int => Int, a: Int, b: Int)Intdef self(x: Int): Int = x //> self: (x: Int)Intdef square(x: Int): Int = x * x //> square: (x: Int)Intdef powerOfTwo(x: Int): Int = if (x == 0) 1 else 2 * powerOfTwo(x - 1)//> powerOfTwo: (x: Int)Intdef sumInts(a: Int, b: Int): Int = sum(self, a, b)//> sumInts: (a: Int, b: Int)Intdef sumSquared(a: Int, b: Int): Int = sum(square, a, b)//> sumSquared: (a: Int, b: Int)Intdef sumPowersOfTwo(a: Int, b: Int): Int = sum(powerOfTwo, a, b)//> sumPowersOfTwo: (a: Int, b: Int)Intprintln(sumInts(1, 5)) //> 15println(sumSquared(1, 5)) //> 55println(sumPowersOfTwo(1, 5)) //> 62 示例說明:- sumInts函數:求連續整數的和
- sumSquared函數:求連續整數的平方和
- sumPowersOfTwo函數:求連續整數的關于2的冪次和
占位符語法
使用下劃線作為一個或多個參數的占位符,只要每個參數在函數字面量內僅出現一次。 println("Testing, Scala!") //> Testing, Scala!val numList = List(-3, -5, 1, 6, 9) //> numList : List[Int] = List(-3, -5, 1, 6, 9)numList.filter(x => x > 0) //> res0: List[Int] = List(1, 6, 9)numList.filter(_ > 0) //> res1: List[Int] = List(1, 6, 9) 示例說明: 當采用下劃線的表示方法時,對于列表numList中的每個元素,都會依次傳入用來替換下劃線。 比如,首先傳入-3,判斷-3>0是否成立,是則把該值放入結果集合,否則舍棄;然后傳入-5,判斷-5>0是否成立,依此類推。柯里化
柯里化函數(Curried Funciton)把具有多個參數的函數轉化為一條函數鏈,每個節點上是單一參數。 在函數式編程中,可以基于一些通用性的函數,利用柯里化函數等來構造新函數,而不需要重新定義新函數。 示例: def add(x: Int, y: Int) = x + y //> add: (x: Int, y: Int)Intadd(1, 2) //> res0: Int = 3def addCurried(x: Int)(y: Int) = x + y //> addCurried: (x: Int)(y: Int)IntaddCurried(1)(2) //> res1: Int = 3val addOne = addCurried(1)_ //> addOne : Int => Int = TestScala$$$Lambda$8/6738746@7cf10a6faddOne(2) //> res2: Int = 3
示例說明: 函數add和addCurried的函數定義時等價的。 “addCurried(1)_”的下劃線是通配后面所有的參數列表。 遞歸
在函數式編程中利用遞歸函數(Recursive Funtion)實現循環。 def factorial(n: Int): Int =if (n <= 0) 1else n * factorial(n - 1) //> factorial: (n: Int)Intfactorial(5) //> res0: Int = 120尾遞歸
在尾遞歸函數(Tail?Recursive Funtion)中所有遞歸形式的調用都出現在函數的末尾。 當編譯器檢測到一個函數調用時尾遞歸的時候,它就覆蓋當前的活動記錄,而不是在棧中去創建一個新的。 Scala編譯器不會主動進行尾遞歸優化,需要“@annotation.tailrec”來 告知Scala編譯器 package testscalaobject TestScala {def main(args: Array[String]) {println("Testing, Scala!")val res = factorial2(5, 1)println(res)}@annotation.tailrecdef factorial2(n: Int, m: Int): Int =if (n <= 0) melse factorial2(n - 1, m * n)}示例:求整數a到b的相加之和
def sum(f: Int => Int)(a: Int)(b: Int): Int = {@annotation.tailrecdef loop(n: Int)(acc: Int): Int = {if (n > b) {println(s"n=${n},acc=${acc}")acc} else {println(s"n=${n},acc=${acc}")loop(n + 1)(acc + f(n))}}loop(a)(0)} //> sum: (f: Int => Int)(a: Int)(b: Int)Intsum(x => x)(1)(5) //> n=1,acc=0//| n=2,acc=1//| n=3,acc=3//| n=4,acc=6//| n=5,acc=10//| n=6,acc=15//| res0: Int = 15sum(x => x * x)(1)(5) //> n=1,acc=0//| n=2,acc=1//| n=3,acc=5//| n=4,acc=14//| n=5,acc=30//| n=6,acc=55//| res1: Int = 55sum(x => x * x * x)(1)(5) //> n=1,acc=0//| n=2,acc=1//| n=3,acc=9//| n=4,acc=36//| n=5,acc=100//| n=6,acc=225//| res2: Int = 225val square = sum(x => x * x)_ //> square : Int => (Int => Int) = TestScala$$$Lambda$13/757108857@6bdf28bbsquare(1)(5) //> n=1,acc=0//| n=2,acc=1//| n=3,acc=5//| n=4,acc=14//| n=5,acc=30//| n=6,acc=55//| res3: Int = 55?
轉載于:https://www.cnblogs.com/anliven/p/10041890.html
總結
以上是生活随笔為你收集整理的Scala - 快速学习08 - 函数式编程:高阶函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CNCF宣布Envoy项目正式毕业
- 下一篇: re:Invent解读:没想到你是这样的