1. 程式人生 > >Scala學習日誌——一切從使用開始

Scala學習日誌——一切從使用開始

摘要

Scala,被稱為可伸展的語言。由於其的完全面向物件性卻又融合函數語言程式設計,使其程式碼十分優美,簡潔。他可以用寥寥幾行就完成在Java中大量程式碼才能完成的操作。且簡單易懂,有效的增強程式碼的可讀性,並減少出錯的可能。由於近年來大資料方便Spark一片大好,可以說學大資料必須要學Spark。而總所周知的,Spark的底層是由Scala進行編寫的,因此Spark對Scala的適應性是十分好的。因此學習Spark必須先學Scala。不例外的,我也走上了這條道路。

基本中的基本

我不願意將scala吹得天花亂墜,這裡主要是一些小的實踐來明確scala究竟是如何用的,更深的東西,等能用了再慢慢研究。

1、定義一些變數

首先,我是從java轉向scala的,因此當我使用val時,感覺十分的彆扭。不過同理,如果你是從函數語言程式設計語言轉入scala的,你同樣會覺得var的使用簡直就是中褻瀆。這裡,無論你是從什麼語言轉向scala,我都建議你多使用val,儘量能夠達到整個程式中不出現var。為什麼這麼說,我一個小例子來進行說明:

//宣告函式語法:def 函式名(引數1:引數型別):返回值型別=
def printArgs(args: Array[String]): Unit = {  
    var i = 0  
    while (i < args.length) {
        i += 1
} }

這個方法是使用var 並因此屬於指令式風格

def printArgs(args: Array[String]): Unit = {
  for (arg <- args)   
      println(arg) 
  }

這是沒有使用var的,更函式式。可以簡單的看出,不使用var令程式碼更加的簡單易懂,減少了出錯的可能。

2. 使用Array與ArrayBuffer

  • 宣告定長陣列:
    val arr=Array(1,2,3,4) //arr這時為 Array(1,2,3,4)
    如果要宣告一個空的陣列,可以使用:
    val arr2=new Array[Int]

    Array排序
    println(util.Sorting.quickSort(a))
  • 呼叫陣列的方法:
    arr2(0)="3" //修改arr2(0)的值,注意使用的是()而不是[]

  • 很多時候,我們並不確定陣列的長度。亦或許我們要向陣列中新增元素,這時就需要可變長的陣列,在java中,有ArrayList,scala中則是ArrayBuffer。下面是一些ArrrayBuffer的使用方法,註釋寫得挺詳細的,我便不贅述了。

    //變長陣列:ArrayBuffer
    val b=ArrayBuffer[Int]()  //宣告
    val c=new ArrayBuffer[Int]()

    /*
    高效操作
     */

    //在ArrayBuffer後新增一個元素
    b += 1;
    for(i <- b) println(i)

    //新增多個元素
    b += (3,5,7,9)
    println(b)

    //新增任意集合
    b ++= Array(2,4,6,8)
    println(b)

    //移除元素
    b.trimEnd(3)
    println(b)
    b.trimStart(3)
    println(b)

    /*
   並不那麼高效的操作
    */
    //在下標2前插入6
    b.insert(2,6)
    println(b)
    b.insert(2,7,8,9) //下標2前插入多個數據

    //移除元素
    b.remove(2) //移除下標2
    b.remove(2,3)  //第二個引數表示要移除多少元素
    for(i <- 0 until (b.length,2))  println(b(i))    //兩個一跳
    for(i <- (0 until b.length).reverse) println(b(i))  //從後向前迴圈


    //移除陣列中第一個負數之外的所有負數
    val a=Array(1,-1,3,-3,-5,-7,3,4,6,8)
    //首先取到所有不被移除的下標
    var first=true
    val indexs = for(i<- 0 until a.length if first || a(i)>= 0) yield{
      if( a(i)<0 )first=false ; i
    }
    println(indexs)
    //將元素向前移動,再去掉尾部
    for(j <- 0 until indexs.length ) {
      a(j)=a(indexs(j))
      a.toBuffer.trimEnd(a.length-indexs.length)
    }
    for(i <- a  ) print(i+"\t")
    println()

    println(a.sum+" "+a.max+" "+a.min)  //sum輸出整個陣列的和(必須是數值型別 ),max最大,min最小值,不用非是數值


    //陣列排序
    //ArrayBuffer排序
    println(b.sorted)

    val c=ArrayBuffer(1,2,3,4,5,6,7,8,9)
    println(c.sortWith(_>_))  //自己給定演算法
  • Array與ArrayBuffer之間可以自由轉換。比如上面例子中c.toArray便可將c轉化為Array,b.toBuffer便可將b轉化為ArrayBuffer。因此當不確定陣列長度時,可以先使用ArrayBuffer,操作完成後,再轉換回Array便可。

3、宣告函式

宣告函式沒什麼可說的,直接上程式碼了,註釋同樣挺詳細的

//宣告函式語法:def 函式名(引數1:引數型別):返回值型別=
def printArgs(args: Array[String]): Unit = {  
    var i = 0  
    while (i < args.length) {
        i += 1  
    }
 } 
//函式引數可以有預設值,不過呼叫時如果傳入引數的話就會替代。
  def decorate(str: String, left: String = "[", right: String = "]") = left + str + right

  //變長引數函式:可以傳入不定量的引數,但是不代表著可以傳入該引數的陣列
  def sum(args : Int*): Int ={
    var result = 0
    for(arg <- args) result += arg
    result
  }
  //遞迴呼叫
  def recursiveSum(args : Int*) :Int={
    if(args.length == 0) 0
    else args.head + recursiveSum(args.tail:_*)   
    //tail是所有其他元素的序列,使用:_*來將它轉化為引數序列
  }

4、控制語句

類似if,while這些我認為沒有太大說的必要。怎麼使用其實很簡單,這裡我只重點說說for迴圈,因為小生認為scala的for迴圈十分的強大且優美:

for(i <- 0 to 3) println(i)
    //for迴圈中可以很複雜,可以有多個引數,實際就像java中的兩層迴圈,一層是i,一層是j。
    //i <- 0 to 3的意思是i遍歷0到3
    //scala的for迴圈中還能加 if 只有能滿足 if 的才會被遍歷到
    for (i <- 0 to 3; j <- 0 to 3 if (i != 0 && i != j)) print(decorate((i * 10 + j).toString, "<<", ">>") + " ")
    //輸出<<10>> <<12>> <<13>> <<20>> <<21>> <<23>> <<30>> <<31>> <<32>>


    //還有一種for迴圈推到式,當在for迴圈開頭使用yield時,
    // 會生成一個集合,然後將每個迴圈到的值放入集合
    val a=for (i <- 0 to 3; j <- 0 to 3 if (i != 0 && i != j)) yield decorate((i * 10 + j).toString, "<<", ">>")
    //a=Vector(<<10>>, <<12>>, <<13>>, <<20>>, <<21>>, <<23>>, <<30>>, <<31>>, <<32>>)

5、map,tuple

map對映是一種特殊的tuple,這裡我只說說他們的用法。同樣在程式碼裡:

  • map的用法:
//構造map
    val scores = Map("wrm" -> 21,"wln" -> 21,"xxw" -> 11)
    //預設構造的是不可變的Map,如需要可變的Map,使用:
    val scores1 =scala.collection.mutable.Map("wrm" -> 21,"wln" -> 21,"xxw" -> 11)

    //如果想先建立一個空的map,需要指定一個Map實現(new) 和給出型別引數
    val nullmap=new HashMap[String,Int]

    println(scores)

    //通過鍵找到值:
    println(scores("wrm"))
    //判斷是否有某個值,有便獲得
    val hasValue=if(scores.contains("zzz")) scores("zzz") else 0
    println(hasValue)
    //有種便捷的寫法:
    val simplaValue=scores.getOrElse("xxw",0)
    println(simplaValue)


    //map的更新
    //更新某個對映的值:=  如果存在就更新,不存在就新增
    scores1("xxw")=100
    scores1("yxs")=30
    println(scores1)

    //+=用來新增多個關係
    scores1+=("yjl" -> 22,"wzx" ->21)
    println(scores1)
    //-=用來移除某個關係
    scores1-=("yxs")
    println(scores1)

    //上述是對於可變MAP(scala.collection.mutable.Map)
    //對於不可變map,可以這樣操作,
    val newmap=scores + ("wrm" -> 23,"wzx" ->21)  //跟新過的map,如果key存在就更新,不存在就新增
    println(newmap)
    //其餘操作同理,看似建立了過多的val效率低又浪費資源,其實並非如此——老的對映和新的對映共享大部分資源。(因為他們是不可變的!)


    //map的迭代:
    //十分簡單的一個方式
    for((k,v) <- newmap) println("key:"+k+" value:"+v)
    //要反轉一個對映——交換k,v位置,可用
    val changeMap=for((k,v) <- newmap) yield (v,k)
    println(changeMap )
  • tunple的用法
    //Tuple的每個引數的資料型別都是不同的如:
    val t=(1,3.14,"Fred")
    //可以使用t._1,t._2或t _1,t _2來進行呼叫
    println(t _2)   //3.14
    //可以通過模式匹配來使用Tuple
    val (first,second,third)=t
    println(first+"\t"+second+"\t"+third)   //1 3.14    Fred
    //在不需要匹配所有項時,不需匹配的項可以使用_忽略
    val (a,b,_)=t
    println(a+"\t"+b)   //1 3.14

    //拉鍊操作
    val symbols=Array("<","-",">")
    val counts=Array(2,10,2)
    //可以將兩個Array壓縮為一個Tuple
    val pairs=symbols.zip(counts)

    //這些對偶可以被一起處理
    for((s,n) <- pairs) Console.print(s * n)  //<<---------->>

今天就進行到這裡,接下來會進行更加深入的研究學習。希望這篇部落格能幫到有需要的朋友!