Scala入门到精通——第十三节 高阶函数
本節主要內容
1. 高階函數簡介
高階函數主要有兩種:一種是將一個函數當做另外一個函數的參數(即函數參數);另外一種是返回值是函數的函數。這兩種在本教程的第五節 函數與閉包中已經有所涉及,這里簡單地回顧一下:
(1)函數參數
- 1
(2)返回值是函數的函數
//高階函數可以產生新的函數,即我們講的函數返回值是一個函數 //(Double)=>((Double)=>Double) scala> def multiplyBy(factor:Double)=(x:Double)=>factor*x multiplyBy: (factor: Double)Double => Doublescala> val x=multiplyBy(10) x: Double => Double = <function1>scala> x(50) res33: Double = 500.0Scala中的高階函數可以說是無處不在,這點可以在scala中的API文檔中得到驗證,下圖給出的是Array數組的需要函數作為參數的API:
例如flatMap方法,下面是其API的詳細內容:
- 1
2. Scala中的常用高階函數
1 map函數
所有集合類型都存在map函數,例如Array的map函數的API具有如下形式:
List類型:
scala> val list=List("Spark"->1,"hive"->2,"hadoop"->2) list: List[(String, Int)] = List((Spark,1), (hive,2), (hadoop,2))//寫法1 scala> list.map(x=>x._1) res20: List[String] = List(Spark, hive, hadoop) //寫法2 scala> list.map(_._1) res21: List[String] = List(Spark, hive, hadoop)scala> list.map(_._2) res22: List[Int] = List(1, 2, 2)Map類型:
//寫法1 scala> Map("spark"->1,"hive"->2,"hadoop"->3).map(_._1) res23: scala.collection.immutable.Iterable[String] = List(spark, hive, hadoop)scala> Map("spark"->1,"hive"->2,"hadoop"->3).map(_._2) res24: scala.collection.immutable.Iterable[Int] = List(1, 2, 3)//寫法2 scala> Map("spark"->1,"hive"->2,"hadoop"->3).map(x=>x._2) res25: scala.collection.immutable.Iterable[Int] = List(1, 2, 3)scala> Map("spark"->1,"hive"->2,"hadoop"->3).map(x=>x._1) res26: scala.collection.immutable.Iterable[String] = List(spark, hive, hadoop)2 flatMap函數
//寫法1 scala> List(List(1,2,3),List(2,3,4)).flatMap(x=>x) res40: List[Int] = List(1, 2, 3, 2, 3, 4)//寫法2 scala> List(List(1,2,3),List(2,3,4)).flatMap(x=>x.map(y=>y)) res41: List[Int] = List(1, 2, 3, 2, 3, 4)3 filter函數
scala> Array(1,2,4,3,5).filter(_>3) res48: Array[Int] = Array(4, 5)scala> List("List","Set","Array").filter(_.length>3) res49: List[String] = List(List, Array)scala> Map("List"->3,"Set"->5,"Array"->7).filter(_._2>3) res50: scala.collection.immutable.Map[String,Int] = Map(Set -> 5, Array -> 7)4 reduce函數
//寫法1 scala> Array(1,2,4,3,5).reduce(_+_) res51: Int = 15scala> List("Spark","Hive","Hadoop").reduce(_+_) res52: String = SparkHiveHadoop//寫法2 scala> Array(1,2,4,3,5).reduce((x:Int,y:Int)=>{println(x,y);x+y}) (1,2) (3,4) (7,3) (10,5) res60: Int = 15scala> Array(1,2,4,3,5).reduceLeft((x:Int,y:Int)=>{println(x,y);x+y}) (1,2) (3,4) (7,3) (10,5) res61: Int = 15scala> Array(1,2,4,3,5).reduceRight((x:Int,y:Int)=>{println(x,y);x+y}) (3,5) (4,8) (2,12) (1,14) res62: Int = 155 fold函數
scala> Array(1,2,4,3,5).foldLeft(0)((x:Int,y:Int)=>{println(x,y);x+y}) (0,1) (1,2) (3,4) (7,3) (10,5) res66: Int = 15scala> Array(1,2,4,3,5).foldRight(0)((x:Int,y:Int)=>{println(x,y);x+y}) (5,0) (3,5) (4,8) (2,12) (1,14) res67: Int = 15scala> Array(1,2,4,3,5).foldLeft(0)(_+_) res68: Int = 15scala> Array(1,2,4,3,5).foldRight(10)(_+_) res69: Int = 25// /:相當于foldLeft scala> (0 /: Array(1,2,4,3,5)) (_+_) res70: Int = 15scala> (0 /: Array(1,2,4,3,5)) ((x:Int,y:Int)=>{println(x,y);x+y}) (0,1) (1,2) (3,4) (7,3) (10,5) res72: Int = 156 scan函數
//從左掃描,每步的結果都保存起來,執行完成后生成數組 scala> Array(1,2,4,3,5).scanLeft(0)((x:Int,y:Int)=>{println(x,y);x+y}) (0,1) (1,2) (3,4) (7,3) (10,5) res73: Array[Int] = Array(0, 1, 3, 7, 10, 15)//從右掃描,每步的結果都保存起來,執行完成后生成數組 scala> Array(1,2,4,3,5).scanRight(0)((x:Int,y:Int)=>{println(x,y);x+y}) (5,0) (3,5) (4,8) (2,12) (1,14) res74: Array[Int] = Array(15, 14, 12, 8, 5, 0)3. SAM轉換
在Java的GUI編程中,在設置某個按鈕的監聽器的時候,我們常常會使用下面的代碼(利用scala進行代碼開發):
var counter=0; val button=new JButton("click") button.addActionListener(new ActionListener{override def actionPerformed(event:ActionEvent){counter+=1} })上面代碼在addActionListener方法中定義了一個實現了ActionListener接口的匿名內部類,代碼中
new ActionListener{override def actionPerformed(event:ActionEvent){}這部分稱為樣板代碼,即在任何實現該接口的類中都需要這樣用,重復性較高,由于ActionListener接口只有一個actionPerformed方法,它被稱為simple abstract method(SAM)。SAM轉換是指只給addActionListener方法傳遞一個參數
button.addActionListener((event:ActionEvent)=>counter+=1)//并提供一個隱式轉換,我們后面會具體講隱式轉換 implict def makeAction(action:(event:ActionEvent)=>Unit){new ActionListener{override def actionPerformed(event:ActionEvent){action(event)} }這樣的話,在進行GUI編程的時候,可以省略非常多的樣板代碼,使代碼更簡潔。
4. 函數柯里化
在函數與閉包那一節中,我們定義了下面這樣的一個函數
//mutiplyBy這個函數的返回值是一個函數 //該函數的輸入是Doulbe,返回值也是Double scala> def multiplyBy(factor:Double)=(x:Double)=>factor*x multiplyBy: (factor: Double)Double => Double//返回的函數作為值函數賦值給變量x scala> val x=multiplyBy(10) x: Double => Double = <function1>//變量x現在可以直接當函數使用 scala> x(50) res33: Double = 500.0上述代碼可以像這樣使用:
scala> def multiplyBy(factor:Double)=(x:Double)=>factor*x multiplyBy: (factor: Double)Double => Double//這是高階函數調用的另外一種形式 scala> multiplyBy(10)(50) res77: Double = 500.0那函數柯里化(curry)是怎么樣的呢?其實就是將multiplyBy函數定義成如下形式
scala> def multiplyBy(factor:Double)(x:Double)=x*factor multiplyBy: (factor: Double)(x: Double)Double- 1
即通過(factor:Double)(x:Double)定義函數參數,該函數的調用方式如下:
//柯里化的函數調用方式 scala> multiplyBy(10)(50) res81: Double = 500.0//但此時它不能像def multiplyBy(factor:Double)=(x:Double)=>factor*x函數一樣,可以輸入單個參數進行調用 scala> multiplyBy(10)<console>:10: error: missing arguments for method multiplyBy; follow this method with `_' if you want to treat it as a partially applied funct ionmultiplyBy(10)^錯誤提示函數multiplyBy缺少參數,如果要這么做的話,需要將其定義為偏函數
scala> multiplyBy(10)_ res79: Double => Double = <function1>那現在我們接著對偏函數進行介紹
5. 部分應用函數
在數組那一節中,我們講到,Scala中的數組可以通過foreach方法將其內容打印出來,代碼如下:
scala>Array("Hadoop","Hive","Spark")foreach(x=>println(x)) Hadoop Hive Spark //上面的代碼等價于下面的代碼 scala> def print(x:String)=println(x) print: (x: String)Unitscala> Array("Hadoop","Hive","Spark")foreach(print) Hadoop Hive Spark- 1
那什么是部分應用函數呢,所謂部分應用函數就是指,當函數有多個參數,而在我們使用該函數時我們不想提供所有參數(假設函數有3個函數),只提供0~2個參數,此時得到的函數便是部分應用函數,定義上述print函數的部分應用函數代碼如下:
//定義print的部分應用函數 scala> val p=print _ p: String => Unit = <function1>scala> Array("Hadoop","Hive","Spark")foreach(p) Hadoop Hive Sparkscala> Array("Hadoop","Hive","Spark")foreach(print _) Hadoop Hive Spark在上面的簡化輸出代碼中,下劃線_并不是占位符的作用,而是作為部分應用函數的定義符。前面我演示了一個參數的函數部分應用函數的定義方式,現在我們定義一個多個輸入參數的函數,代碼如下:
//定義一個求和函數 scala> def sum(x:Int,y:Int,z:Int)=x+y+z sum: (x: Int, y: Int, z: Int)Int//不指定任何參數的部分應用函數 scala> val s1=sum _ s1: (Int, Int, Int) => Int = <function3>scala> s1(1,2,3) res91: Int = 6//指定兩個參數的部分應用函數 scala> val s2=sum(1,_:Int,3) s2: Int => Int = <function1>scala> s2(2) res92: Int = 6//指定一個參數的部分應用函數 scala> val s3=sum(1,_:Int,_:Int) s3: (Int, Int) => Int = <function2>scala> s3(2,3) res93: Int = 6在函數柯里化那部分,我們提到柯里化的multiplyBy函數輸入單個參數,它并不會像沒有柯里化的函數那樣返回一個函數,而是會報錯,如果需要其返回函數的話,需要定義其部分應用函數,代碼如下:
//定義multiplyBy函數的部分應用函數,它返回的是一個函數 scala> val m=multiplyBy(10)_ m: Double => Double = <function1>scala> m(50) res94: Double = 500.0總結
以上是生活随笔為你收集整理的Scala入门到精通——第十三节 高阶函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Scala入门到精通——第十一节 Tra
- 下一篇: Scala入门到精通——第十四节 Cas