日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Scala - 快速学习08 - 函数式编程:高阶函数

發布時間:2025/3/18 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Scala - 快速学习08 - 函数式编程:高阶函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

函數式編程的崛起

函數式編程中的“值不可變性”避免了對公共的可變狀態進行同步訪問控制的復雜問題,能夠較好滿足分布式并行編程的需求,適應大數據時代的到來。

函數是第一等公民

  • 可以作為實參傳遞給另外一個函數
  • 可以作為返回值
  • 可以賦值給變量
  • 可以存儲在數據結構里
def greeting() = (name: String) => { s"Hello" + " " + name }//> greeting: ()String => Stringgreeting()("World") //> res0: String = Hello Worlddef greeting2(age: Int) = (name: String) => { s"Hello $name, your age is $age" }//> greeting2: (age: Int)String => Stringgreeting2(29)("Anliven") //> res2: String = Hello Anliven, your age is 29

函數類型和值

在函數式編程中,函數的使用方式和其他數據類型的使用方式完全一致,可以像任何其他數據類型一樣被傳遞和操作。 也就是說,可以像定義變量那樣去定義一個函數,函數也會有“值”,函數的“值”就是“函數字面量(Funciton Literal)”,也稱為函數文字量函數常量。實際上,這個函數字面量其實就是一個匿名函數需要注意:
  • Scala語法要求函數的“值”采用“=>”而不是“=”。
  • 在Scala中,函數類型的格式為 A => B,表示一個接受類型A的參數,并返回類型B的函數。
def test(x: Int): Int = { x + 1 } //> test: (x: Int)Intdef test1(x: Int) = x + 1 //> test1: (x: Int)Intval test2: Int => Int = { (x: Int) => x + 1 } //> test2 : Int => Int = testrepl$$$Lambda$8/290658609@c818063val test3 = { (x: Int) => x + 1 } //> test3 : Int => Int = testrepl$$$Lambda$9/1057941451@75bd9247def test5(x: Int, y: Int) = x + y //> test5: (x: Int, y: Int)Intvar test6 = (x: Int, y: Int) => x + y //> test6 : (Int, Int) => Int = testrepl$$$Lambda$10/1513712028@3cb5cdbatest6(2, 6) 示例說明: 第7行把函數"(x: Int, y: Int) => x + y"作為一個值(函數字面量)賦給test6變量。 由此可見,Scala中的函數和普通變量的使用方式完全一致。

匿名函數

使用匿名函數(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的類型聲明,省略匿名函數的表達式花括號
注意:類型聲明“Int=>Int”和x的類型聲明,不可以同時省略,因為全部省略以后,解釋器也無法推斷出類型。 def test1(x: Int, y: Int): Int = { x + y } //> test1: (x: Int, y: Int)Intdef test2(x: Int, y: Int) = x + y //> test2: (x: Int, y: Int)Int(x: Int, y: Int) => x + y //> res0: (Int, Int) => Int = testrepl$$$Lambda$8/290658609@c818063var test3 = (x: Int, y: Int) => x + y //> test3 : (Int, Int) => Int = testrepl$$$Lambda$9/1057941451@75bd9247var test4: (Int, Int) => Int = (x, y) => { x + y }//> test4 : (Int, Int) => Int = testrepl$$$Lambda$10/2101440631@7dc36524test1(2, 6) //> res1: Int = 8test2(2, 6) //> res2: Int = 8test3(2, 6) //> res3: Int = 8test4(2, 6) //> res4: Int = 8

閉包

閉包是一個比較特殊的函數,反映了一個從開放到封閉的過程,返回值依賴于聲明在函數外部的一個或多個變量。
  • 如果引用的變量是自由變量,沒有綁定具體的值,那么此時這個函數是“開放的”。
  • 如果引用的自由變量被綁定具體的值后,不再是“自由變量”,從而構成一個封閉的函數,那么此時這個函數“被關閉”了。
函數引用的外部變量,必須在函數外部給出值。 閉包示例-1: var more = 1 //> more : Int = 1val addMore = (x: Int) => x + more //> addMore : Int => Int = testrepl$$$Lambda$9/240650537@1cd072a9addMore(10) //> res0: Int = 11more = 9addMore(10) //> res1: Int = 19 示例說明: 函數定義“val addMore = (x: Int) => x + more”中,引用了沒有在函數中定義的外部變量more,而more是一個自由變量,還沒有綁定具體的值,此時這個函數是“開放的”;而變量x是一個已在函數中明確定義的變量,只有在調用的時候才被賦值。 外部變量more確定具體值(“?var more = 1”)以后,那么函數addMore中的more變量也就被綁定具體值了,不再是“自由變量”,此時這個函數是“關閉的”。 另外,每次addMore函數被調用時都會創建一個新閉包。每個閉包都會訪問閉包創建時活躍的more變量。 閉包示例-2: def plusStep(step: Int) = (num: Int) => num + step//> plusStep: (step: Int)Int => Intval myFunc = plusStep(3) //> myFunc : Int => Int = testrepl$$$Lambda$8/209813603@2f7c7260println(myFunc(10)) //> 13 示例說明: step是一個自由變量,它的值只有在運行的時候才能確定,num的類型是確定的,num的值只有在調用的時候才被賦值。 這樣的函數,被稱為“閉包”,它反映了一個從開放到封閉的過程。

高階函數

用函數作為形參或返回值的函數,稱為高階函數。 也就是說,高階函數就是一個接受其他函數作為參數或者返回一個函數的函數。 高階函數示例-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 - 函数式编程:高阶函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。