天天看點

Scala集合操作中的幾種高階函數

 Scala是函數式程式設計,這點在集合操作中大量展現。高階函數,也就是能夠接收另外一個函數作為參數的函數。

假如現在有一個需要是将List集合中的每個元素變為原來的兩倍,現在來對比Java方式實作和Scala方式實作差別

Java方式實作,先将集合中的每個元素周遊出來,然後再乘以2,塞到另外一個集合中

ArrayList<Integer> list1 = new ArrayList<Integer>();
list1.add(3);
list1.add(5);
list1.add(7);
ArrayList<Integer> list2 = new ArrayList<Integer>();
for (Integer elem : list1) {
    list2.add(elem * 2);
}
System.out.println(list2);
      

  

Scala方式實作

val list1 = List(3, 5, 7)
val list2 = list1.map(multiple) //map高階函數,能夠接收另外一個函數
def multiple(n1: Int): Int = { 
    2 * n1 
}
println(list2)      

可以發現相對于Java的實作方式,Scala中更偏向于使用高階函數來解決問題,并且也簡化了很多。

或許你會有些許疑問,這是什麼鬼,這沒有簡化到哪裡呀!的确,但是這裡隻是小小的示範。  

Scala中常用的高階函數有如下幾種

1.映射函數(map)

map函數

Scala集合操作中的幾種高階函數

 小注:在Scala中的泛型是表示方法是“[]”,java中的泛型表示方式是“<>”。map函數存在于所有集合類型中,包括在String中。

 現在再看前面的執行個體,它是這樣來執行的

  1.   首先依次周遊list1集合的元素
  2.   将各個元素傳遞給Multiple函數,計算并傳回
  3.   将傳回結果放到一個新的集合中,并賦給list2
  4.   輸出結果

 為了能夠更好的了解,嘗試編寫一個List,來模拟List

object Demo_021{
  def main(args: Array[String]): Unit = {
    val myList = MyList()
    val myList2 = myList.map(multiple)  //調用map高階函數,并傳入mutiple函數
    println("myList2=" + myList2)
    println("myList=" + myList.list1)
  }
  def multiple(n1: Int): Int = {
      2 * n1
  }
}
//伴生類,模拟List
class MyList {
    var list1 = List(3, 5, 7)
    var list2 = List[Int]()
    //map高階函數,接收另外一個函數作為參數,
    // f:Int=>Int : f表示是函數,:Int表示所傳入的函數f的參數類型,必須是Int型,=>Int表示所傳入函數f的傳回值為Int
    // : List[Int] :表示Map函數的傳回值為List[Int]
    def map(f:Int=>Int): List[Int] = {
      for (item<-list1) {
        list2 = list2 :+ f(item)  //f(item) 表示調用所傳入的函數,每次執行都會将傳回寫過寫入到list2中
      }
      list2 //傳回list2,未明确指定傳回值,以函數最後一行的執行結果作為傳回值
    }
  }
//伴生對象
  object MyList {
    //使用apply方式執行個體化
    def apply(): MyList = new MyList()
  }
      

運作結果:

Scala集合操作中的幾種高階函數

 模拟有些拙劣,但是基本能夠說明問題,map方法在List底層所實作時,也是逐個周遊并執行所傳入的函數,最後傳回執行結果集合

 下面是List中map函數的源碼,實際List集合底層在調用map方法的時候所做的操作和上面類似

Scala集合操作中的幾種高階函數

使用執行個體1:将 val names = List("Alice", "Bob", "Nick") 中的所有單詞,全部轉成字母大寫,傳回到新的List集合中.

object Demo_022 {
  def main(args: Array[String]): Unit = {
    val names = List("Alice", "Bob", "Nick")
    val names2 = names.map(upper)
    println("names=" + names2)
  }
  def upper(s:String): String = {
    s.toUpperCase
  }
}
      

  執行結果:

Scala集合操作中的幾種高階函數

2.扁平化(flatMap)

flatmap:所謂扁平化,就是将集合中的每個元素的子元素映射到某個函數并傳回新的集合。

執行個體:

object Demo_022 {
  def main(args: Array[String]): Unit = {
    val names = List("Alice", "Bob", "Nick")
    //相當于在原來map高階函數的基礎上做了二次循環,将元素進一步打散
    val names2 = names.flatMap(upper)
    println("names=" + names2)
  }
  def upper(s:String): String = {
    s.toUpperCase
  }
}
      

  運作結果:

Scala集合操作中的幾種高階函數

3.過濾(filter)

 filter:将符合要求的資料(篩選)放置到新的集合中

應用案例:将  val names = List("Alice", "Bob", "Nick") 集合中首字母為'A'的篩選到新的集合。

object Demo_025 {
  def main(args: Array[String]): Unit = {
    val names = List("Alice", "Bob", "Nick")

    def startA(s: String): Boolean = {
      s.startsWith("A")
    }

    val names2 = names.filter(startA) //表示調用filter高階函數
    println("names=" + names2)

  }
}
      

 運作結果:

Scala集合操作中的幾種高階函數

還有更為簡潔的操作:

//    val names2: List[String] = names.filter((x:String)=>x.startsWith("A"))
      val names2: List[String] = names.filter(_.startsWith("A"))
      

 filter函數在執行過程中,類似于map函數,将符合條件的篩選出來放到一個集合中。

4.化簡(reduce、reduceLeft、reduceRight)

化簡:将二進制函數引用于集合中的函數。有三種類型的函數,reduce、reduceLeft和reduceRight,其中reduce等同于reduceLeft。

reduceLeft(f) 接收的函數需要的形式為 op: (B, A) => B): B,

reduceleft(f) 的運作規則是 從左邊開始執行将得到的結果傳回給第一個參數,然後繼續和下一個元素運作,将得到的結果繼續傳回給第一個參數,繼續.。

reduceRight的運作規則和reduceRight類似,隻是從右往左執行

Scala集合操作中的幾種高階函數

執行個體1:val list = List(1,2,3,4,5) , 求出list的和

object Demo_026 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4, 5)
    def sum(n1: Int, n2: Int): Int = {
      n1 + n2
    }
    val res1 = list.reduceLeft(sum)
    println("res=" + res1)
  }
}       

輸出為60。

執行個體2:觀察reduce、reduceRight和reduceLeft在求List(1, 2, 3, 4 ,5)中元素內插補點時的表現

object Demo_027 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4 ,5)
    def minus( num1 : Int, num2 : Int ): Int = {
      num1 - num2
    }
    println(list.reduceLeft(minus)) // 輸出-13
    println(list.reduceRight(minus)) //輸出3
    println(list.reduce(minus)) //輸出-13

  }
}       

運作結果為

Scala集合操作中的幾種高階函數

 綜述:reduce等同于reduceLeft、執行規則從左向右,而reduceRight執行規則是從右向左。

另外,還可以使用化簡來求出一個集合的最值

object Demo_027 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4 ,5)
    def max( num1 : Int, num2 : Int ): Int = {
       if(num1<num2){
           num2
       }else{
           num1
       }
    }
    println(list.reduceLeft(max)) // 求list中的最大值
  }
}
      

簡化形式是:

val result: Int = list.reduceLeft((num1,num2)=>{if(num1<num2) num2 else num1})      

5.折疊(foldLeft、foldRight、fold)

 fold函數将上一步傳回的值作為函數的第一個參數繼續傳遞參與運算,直到list中的所有元素被周遊。有三種函數形式:fold、foldLeft和folderRight。

fold函數在使用上基本和reduce函數在使用上基本相同,甚至reduceLeft函數的底層,就是調用foldLeft函數

Scala集合操作中的幾種高階函數

觀察如下執行個體

object Demo_028 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4)
    def minus(n1: Int, n2: Int): Int = {
      n1 - n2
    }
    println(list.foldLeft(5)(minus))

    println(list.foldRight(5)(minus))      
} }      

  輸出結果為:

Scala集合操作中的幾種高階函數

它的執行過程是這樣的:

Scala集合操作中的幾種高階函數
Scala集合操作中的幾種高階函數

另外foldLeft和foldRight 縮寫方式分别是:/:和:\

object Demo_028 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4)
    def minus(n1: Int, n2: Int): Int = {
      n1 - n2
    }
    println((5 /: list)(minus))   //等價于list.foldLeft(5)(minus)
    println((list :\ 5)(minus)) //list.foldRight(5)(minus)
  }
}
      

  可以使用folderLeft統計字母出現的次數,還可以用來統計文本中單詞出現的次數

6.掃描(scanLeft、scanRight)

 掃描,即對某個集合的所有元素做fold操作,但是會把産生的所有中間結果放置于一個集合中儲存

object Demo_029 {
  def main(args: Array[String]): Unit = {
    def minus( num1 : Int, num2 : Int ) : Int = {
      num1 - num2
    }
    //5 (1,2,3,4,5) =>(5,4,2,-1,-5,-10)
    val i8 = (1 to 5).scanLeft(5)(minus) //IndexedSeq[Int]
    println(i8)
    
    def add( num1 : Int, num2 : Int ) : Int = {
      num1 + num2
    }
    //5 (1,2,3,4,5) =>(5,6,8, 11,15,20)
    val i9 = (1 to 5).scanLeft(5)(add) //IndexedSeq[Int]
    println(i9)

  }
}
      
Scala集合操作中的幾種高階函數

觀察另外一個執行個體

object Demo_030 {
  def main(args: Array[String]): Unit = {
      def test(num1:Int,num2:Int): Int ={
          num1 * num2
      }
     var result=(1 to 3).scanLeft(3)(test)
     println(result)
  }
}
      

 運作結果

Scala集合操作中的幾種高階函數

 綜述,scanLeft執行類似于folderLeft,隻是它會将中間結果緩存下來。