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中的泛型是表示方法是“[]”,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()
  }

  

運行結果:

 模擬有些拙劣,但是基本能夠說明問題,map方法在List底層所實現時,也是逐個遍歷並執行所傳入的函數,最后返回執行結果集合

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

 

使用實例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
  }
}

  執行結果:

 

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

  運行結果:

 

 

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)

  }
}

 運行結果:

 

還有更為簡潔的操作:

//    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類似,只是從右往左執行

 

 

 

實例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

  }
} 

運行結果為

 綜述: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函數

觀察如下實例

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

  輸出結果為:

 

 

它的執行過程是這樣的:

 

 

 

 

 

另外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)

  }
}

  輸出結果為:

 

 

觀察另外一個實例

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

 運行結果

 綜述,scanLeft執行類似於folderLeft,只是它會將中間結果緩存下來。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM