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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Scala高阶函数详解

發布時間:2025/1/21 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Scala高阶函数详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

概述

高階函數主要有兩種:一種是將一個函數當做另外一個函數的參數(即函數參數);另外一種是返回值是函數的函數。

用函數作為形參或返回值的函數,稱為高階函數。

1)使用函數作為參數

//函數參數,即傳入另一個函數的參數是函數

//((Int)=>String)=>String

scala> def convertIntToString(f:(Int)=>String)=f(4)

convertIntToString: (f: Int => String)String

?

scala> convertIntToString((x:Int)=>x+" s")

res32: String = 4 s

(2)返回值是函數

//高階函數可以產生新的函數,即我們講的函數返回值是一個函數

//(Double)=>((Double)=>Double)

scala> defmultiplyBy(factor:Double)=(x:Double)=>factor*x

multiplyBy: (factor: Double)Double => Double

?

scala> val x=multiplyBy(10)

x: Double => Double = <function1>

?

scala> x(50)

res33: Double = 500.0

Scala中的高階函數可以說是無處不在,這點可以在Scala中的API文檔中得到驗證,下圖給出的是Array數組的需要函數作為參數的API?

Scala中的常用高階函數

  • map函數
  • 所有集合類型都存在map函數,例如Arraymap函數的API具有如下形式:

    def map[B](f: (A) ? B): Array[B]

    用途:Builds a new collection by applying a functiontoall elements of this array.

    B的含義:the element typeof the returned collection.

    f的含義:the functionto apply to each element.

    返回:a newarray resulting from applying the given function f to each element of this arrayand collecting the results.

    ?

    //這里面采用的是匿名函數的形式,字符串*n得到的是重復的n個字符串,這是scalaString操作的一個特點

    scala> Array("spark","hive","hadoop").map((x:String)=>x*2)

    res3: Array[String] = Array(sparkspark, hivehive, hadoophadoop)

    ?

    //在函數與閉包那一小節,我們提到,上面的代碼還可以簡化

    //省略匿名函數參數類型

    scala> Array("spark","hive","hadoop").map((x)=>x*2)

    res4: Array[String] = Array(sparkspark, hivehive, hadoophadoop)

    ?

    //單個參數,還可以省去括號

    scala> Array("spark","hive","hadoop").map(x=>x*2)

    res5: Array[String] = Array(sparkspark, hivehive, hadoophadoop)

    ?

    //參數在右邊只出現一次的話,還可以用占位符的表示方式

    scala> Array("spark","hive","hadoop").map(_*2)

    res6: Array[String] = Array(sparkspark, hivehive, hadoophadoop)

    ?

    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)

    ?

  • 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)

    將List中的List打平。

  • 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)

  • reduce函數
  • //寫法1

    scala> Array(1,2,4,3,5).reduce(_+_)

    res51: Int = 15

    ?

    scala> 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 = 15

    ?

    scala> 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 = 15

    ?

    scala> 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 = 15

    ?

  • 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 = 15

    ?

    scala> 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 = 15

    ?

    scala> Array(1,2,4,3,5).foldLeft(0)(_+_)

    res68: Int = 15

    ?

    scala> Array(1,2,4,3,5).foldRight(10)(_+_)

    res69: Int = 25

    ?

    // /:相當于foldLeft

    scala> (0 /: Array(1,2,4,3,5))(_+_)

    res70: Int = 15

    ?

    ?

    scala> (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 = 15

    ?

    fold, foldLeft, and foldRight之間的區別
      主要的區別是fold函數操作遍歷問題集合的順序。foldLeft是從左開始計算,然后往右遍歷。foldRight是從右開始算,然后往左遍歷。而fold遍歷的順序沒有特殊的次序。來看下這三個函數的實現吧(在TraversableOnce特質里面實現)

    由于fold函數遍歷沒有特殊的次序,所以對fold的初始化參數和返回值都有限制。在這三個函數中,初始化參數和返回值的參數類型必須相同。
      第一個限制是初始值的類型必須是list中元素類型的超類。在我們的例子中,我們的對List[Int]進行fold計算,而初始值是Int類型的,它是List[Int]的超類。
      第二個限制是初始值必須是中立的(neutral)。也就是它不能改變結果。比如對加法來說,中立的值是0;而對于乘法來說則是1,對于list來說則是Nil

  • 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)

    ?

    SAM轉換

    javaGUI編程中,在設置某個按鈕的監聽器的時候,我們常常會使用下面的代碼(利用scala進行代碼開發):

    var counter=0;

    val button=new JButton("click")

    button.addActionListener(new ActionListener{

    overridedef 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)

    ?

    //并提供一個隱式轉換,我們后面會具體講隱式轉換

    implictdefmakeAction(action:(event:ActionEvent)=>Unit){

    newActionListener{

    overridedefactionPerformed(event:ActionEvent){action(event)}

    }

    這樣的話,在進行GUI編程的時候,可以省略非常多的樣板代碼,使代碼更簡潔。

    函數柯里化

    在函數與閉包那一節中,我們定義了下面這樣的一個函數

    //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

    即通過(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 formethodmultiplyBy;

    follow this methodwith `_' ifyouwanttotreatitasapartiallyappliedfunct

    ion

    multiplyBy(10)

    ^

    錯誤提示函數multiplyBy缺少參數,如果要這么做的話,需要將其定義為偏函數

    scala> multiplyBy(10)_

    res79: Double => Double = <function1>

    那現在我們接著對偏函數進行介紹。

    部分應用函數

    在數組那一節中,我們講到,Scala中的數組可以通過foreach方法將其內容打印出來,代碼如下:

    scala>Array("Hadoop","Hive","Spark")foreach(x=>println(x))

    Hadoop

    Hive

    Spark

    //上面的代碼等價于下面的代碼

    scala> def print(x:String)=println(x)

    print: (x: String)Unit

    ?

    scala> Array("Hadoop","Hive","Spark")foreach(print)

    Hadoop

    Hive

    Spark

    那什么是部分應用函數呢,所謂部分應用函數就是指,當函數有多個參數,而在我們使用該函數時我們不想提供所有參數(假設函數有3個函數),只提供0~2個參數,此時得到的函數便是部分應用函數,定義上述print函數的部分應用函數代碼如下:

    //定義print的部分應用函數

    scala> val p=print _

    p: String => Unit = <function1>

    ?

    scala> Array("Hadoop","Hive","Spark")foreach(p)

    Hadoop

    Hive

    Spark

    ?

    scala> 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高阶函数详解的全部內容,希望文章能夠幫你解決所遇到的問題。

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