標籤:

征服Scala 1

個人筆記,很多錯誤,歡迎批評。不能支持md真的麻煩呀!

  • 安裝

安裝jdk8,scala,添加系統變數`SCALA_HOME`:

%SCALA_HOME% C:Progra...ScalannPATHt ;%SCALA_HOME%binn

安裝IDEA scala插件,打開idea,新建一個sbt項目,等一會sbt下載scala sdk完成。這裡sbt使用的scala和我們剛在win安裝的scala不是同一個。那個scala是用來在命令提示符中使用的。

在項目右鍵新建一個worksheet,所有的腳本在保存後自動運行在右側顯示結果。

列印nihao:

println ("nihao") //println 方法定義在Predef中,默認importnprintln {"nihao"} //這種情況是scala的一個語法糖,{}和()等價nprintln({"nihao"}) //這裡的{} 和上面的作用不同,這裡的{}表示一個表達式,n //表達式總是先計算(evaluate),最後一行作為表達式的值n //所以這個調用其實是先計算{"nihao"} ,得到nihao傳給println方法nn//為什麼下面的不行?nprintln "nihao"nn//scala允許在調用對象的單參數方法時省略括弧,n//這種情況中間那個標識符(方法名)或者最後標識符 (list tail的tail)稱為操作符。 n//上面這種情況 會被 認為println."nihao",顯然不對。n//如果你要使中綴操作符,那麼 這麼寫: Console pintln "nihao" n//會編譯成 Console.println("nihao")n

總結就是單參數的可以用{} 代替(); 調用對象的單參數 方法可以使用操作符 形式。後面還有一個例子。

Scala代碼分號是可選的,一般不要用分號,這幾乎沒有什麼問題,但是特殊情況下(後綴操作符被當做中綴操作符,後面會說)會有問題,很容易避免。類型放在變數名後面,使用冒號隔開,支持泛型,使用[T]表示具體類型是T.

Scala中分為兩種變數,一種是可變的,使用var聲明,一種是不可變的,使用val聲明。沒有Java的基本類型int等,所有的都是對象,包括Int,Long等。

  • 方法聲明(定義和聲明有什麼區別?)

Scala中,除了方法,一切都是對象!函數也是對象。對象都有類型,函數和方法的區別後面說。方法是在class內部或者腳本中聲明的函數。

聲明方法使用def關鍵字。最後一行表示返回值,return是可選的 ,方法的聲明和調用非常靈活(以下都是在腳本中的代碼):

//define a methodndef func(param : Int):String = { code goes here }n// 多語句才要{},返回值類型都是可選的,scala有強大的類型推導系統nndef f1(p: Int) : Unit = println(p) //,如果沒有返回值 那麼返回值是Unit類型nn//等號=如果省略,那麼表明返回值是Unit類型,即使用副作用的函數。n//這種代碼被稱為 procedure。ndef f2() {n println("nihao")n}nn//如果沒有參數,聲明時可以省略(),調用也必須不能加 (),n//如果聲明有(),調用可以加(),也可以省略。nndef f3 = { 3 }//返回一個3,調用時只能是f3ndef f4() = { 4+4 } //調用時可以是f4,也可以是f4(), f5 = 4+4n

方法調用格式的花樣也很多,主要有兩個方面,省略括弧和省略dot。

  • 方法調用省略括弧:

1 沒有參數的情況已經說了,即只有方法沒有副作用(方法不影響任何對象的狀態)的時候不用括弧。強烈建議有副作用的方法使用(),即使沒有參數。

2 如果方法只有一個參數,那麼{}和()都可以使用。

def pr(in:Int)={val re = in+1; println(re) }n//以下三種均可npr {111}npr (111)npr ({111}) //這個和上面的兩個不一樣,效果是一樣的n

3 對象方法的調用:如果是對象的方法只用一個參數,那麼使用格式是兩種

instance.method(param)ninstance method param //鼓勵第二種,並且,高階函數強烈推薦第二種,n//這種稱為中綴操作符,scala中所有的操作符其實都是方法名,例如 1 + 2,其實是1.+(2)n/*n操作符都是其實都是方法名,分為前綴,中綴,後綴三種。前綴只有 ~, !, + and - 四個。中綴操作符就是出現在兩個對象之間的操作符,後綴操作符就是沒有參數的方法,例如 list tail 等價於 list.tail();後綴操作符是不鼓勵使用,因為編譯器默認是中綴操作符!n*/nnpr 111n/*錯誤!!!因為pr首先不是前綴操作符,那麼只能是對象或者方法,對象後面只能是中最操作符或者後綴操作符,111顯然不是有效的標識符。如果pr是方法,後面的單參數是不允許省略括弧的。必須用{}或者()包圍實參。n到這裡,已經完全清楚了吧。*/n

  • 單個參數的對象方法調用省略dot:

如果對象方法只有一個參數,並且方法沒有副作用,或者參數是一個函數(map filter等高階函數),那麼可以省略dot。這裡方法名稱作 「中綴操作符」。這個特性和隱式轉換是scala編寫DSL的基礎

names foreach (n => println(n))nnames mkString ","noptStr getOrElse "<empty>"nn//Scala 強烈推薦 高階函數使用中綴操作符格式nval names = List("A","b","shanghai")n//do not use this stylennames.map (_.toUpperCase)n// do not use this stylennames.map (_.toUpperCase).filter (_.length > 5)n// right!nnames map (_.toUpperCase) filter (_.length > 5)nnn// wrong! add() 方法有副作用,改變了javaList的內部狀態njavaList add itemnnn//!!!如果方法沒有參數,不能省略dot:nnames.toList n// is the same asnnames toList // 這是後綴操作符,Unsafe!n//因為分號是可選的,編譯器默認toList是一個中綴操作符,n//所以會讀取下一行的第一個變數,一般是一個關鍵字,編譯出錯n

  • import語句

scala的import語句很靈活,可以在任何地方導入class內部外部,方法內部,代碼塊內部,這樣做有一個好處,限制導入方法和對象的scope,防止污染變數。在後面學了implicit隱式轉換後,就知道import scope有多重要了。

import scala.math._ // import everything in math package nimport java.util.{ ArrayList => _, _} //第一個下劃線表示隱藏ArrayList,第二個表示通配符,導入所有nn//默認,scala導入一下,Predef是很多工具方法和常量nnjava.lang._nscala._nscala.Predef._ //推薦看一下Predef的源代碼包括:n//Predef中定義的方法和屬性n//常用方法和類n//列印方法 println等n//一些調試和錯誤方法n//一個特殊的方法表示方法未實現 ndef ??? : Nothing = throw new NotImplementedErrorn//Predef還有大量的隱式轉換和隱n

  • scala的 _

下劃線到處都是,表示初始化,通配符,隱藏,表示只有一個參數的方法的實參,等等,看代碼:

class Reference[T] {n private var contents: T = _ //初始化,T是Int,則contents是0,T是boolean,則是false;Unit則是()nn }nnList(1, 2, 3) foreach (print _ ) //output 123,表示實參nn//在匿名函數中作為參數佔位符:nList(1, 2, 3) map ( _ + 2 ) // _ + 2是一個匿名函數nn//模式匹配中的最後一行作為通配符ncase _ => "this is match anything other than before cases "nnexpr match {nn case List(1,_,_) => " a list with three element and the first element is 1"n case List(_*) => " a list with zero or more elements "n case Map[_,_] => " matches a map with any key type and any value type "n case _ =>n}nnn//import中作為通配符和隱藏符nimport java.util.{ ArrayList => _, _} //第一個下劃線表示隱藏ArrayList,第二個表示通配符,導入所有nn//將方法變為valuenmethod _ // Eta expansion of method into method valuenn//tuple 的訪問ntpl._2 //返回tpl第二個元素,注意,tuple是從1開始的nnn//還有很多高級的概念,目前還不理解,so上給出的答案ndef f[M[_]] // Higher kinded type parameterndef f(m: M[_]) // Existential typen_ + _ // Anonymous function placeholder parameternm _ // Eta expansion of method into method valuenm(_) // Partial function applicationn_ => 5 // Discarded parameterncase _ => // Wild card pattern -- matches anythingnval (a, _) = (1, 2) // same thingnfor (_ <- 1 to 10) // same thingnf(xs: _*) // Sequence xs is passed as multiple parameters to f(ys: T*)ncase Seq(xs @ _*) // Identifier xs is bound to the whole matched sequencenvar i: Int = _ // Initialization to the default valuendef abc_<>! // An underscore must separate alphanumerics from symbols on identifiersnt._2 n

What are all the uses of an underscore in Scala?

  • 函數

先看定義

A function can be invoked with a list of arguments to produce a result.nA function has a parameter list, a body, and a result type. Functions that arenmembers of a class, trait, or singleton object are called methods.nFunctions defined inside other functions are called local functions. Functionsnwith the result type of Unit are called procedures. Anonymous functions innsource code are called function literals. At run time, function literals areninstantiated into objects called function values.

quote from:

Programming in Scala Second Edition.nMartin Odersky - Lex Spoon - Bill Venners

函數由一個參數列表,一個函數體,一個結果類型構成。函數如果作為class,trait或者object(注意,這裡的object是scala特有的單例對象,不是Java中的instance)的成員,那麼這個函數叫方法。

函數定義在別的函數內部叫局部函數。函數返回值是Unit稱為過程(procedures)。

匿名函數是通過函數字面量( ()=>{函數體} )定義的函數。在運行時,函數字面量被實例化對象,叫函數值。

函數和方法的區別,大部分情況下不用在意區別:

  1. 函數是有類型的: (T1, ..., Tn) => U,是trait FunctionN的一個實例對象,函數有一個apply方法,用來實際執行function的函數體。還有toString, andThen ,conpose等方法。

    val fn: Int => String = i => i+"123" //聲明一個函數nfn(3) //實際背後是fn.apply(3);n

  2. scala中除了method,一切都是instance
  3. method只能用def,function可以是val和def
  4. method可以有類型參數[] ,function不能有,函數在聲明時就需要知道類型。

    def fn(p: List[String]) = {...} //is functionnndef m[T](t: List[T]): Map[T] = {...} //METHODn

  5. 將method轉換成function有兩種方法:

    val f1 = m1 _ //下劃線表示參數列表 eta-expansionnval f2: (Int) => Int = m1 //m1 的入參和返回值要和f2的一樣nn// scala可以自動將method轉換為function,如果一個方法需要一個函數作為參數,n//那麼可以直接將m1傳遞給他,不需要 下劃線。n//每一次將方法轉換成function都是得到一個新的function object。n

  6. function既然是一個instance,那麼編譯成class文件會有一個class文件。

  • => 的作用

n1. 在一個值中,表示一個函數字面量。例如(x: Int) => x * 2表示一個匿名函數,接收一個整數,返回參數乘以2的結果。ntn2. 在類型中types:表示Function<N> trait。例如:nscala> val f: Function1[Int,String] = myInt => "my int: "+myInt.toStringnf: Int => String = <function1>ntnscala> val f2: Int => String = myInt => "my int v2: "+myInt.toStringnf2: Int => String = <function1> n//myInt 表示函數的參數,紅色的是f2的類型,藍色是一個匿名函數,表示函數體。n//上面的f2和f1類型相同。注意,匿名函數沒有參數也要括弧 ()=>{};() => Unit表示沒有返回值的函數即,即Function0[Unit] trait的一個對象,利用副作用的函數。ntn3. 在函數的參數聲明中使用=>(e.g. def f(param: => T))表示這個參數是"by-name parameter",表示這個參數只有在函數體中包含這個參數的語句被執行才會被evaluate。n這個特點叫call-by-name,param可以是一個代碼塊,甚至函數,在傳遞給f時不會evaluate,只有f函數體內部出現param時,param才會被執行。ndef now()={println("nano time:");System.nanoTime}nnscala> def callByName(p: => Long):Long = {println("call-by-name:"+p);p;}ntcallByName: (p: => Long)Longntnscala> def justCall(p : Long) :Long = {println("just-call:"+p);p;}ntjustCall: (p: Long)Longntnscala> callByName(now())ntnano time:ntcall-by-name:5664511571389ntnano time:ntres2: Long = 5664511727048ntnscala> justCall(now())ntnano time:ntjust-call:5667489483159ntres3: Long = 5667489483159nt ntn4. 在case語句中,=> 分隔模式和返回表達式。nn此外,() => Unit means: "Type function that takes no parameters and return nothing"。n

  • call-by-name

參見上一小節的第三點例子

  • 常用數據容器

大部分數據容器都有可變和不可變的兩種。默認都在Predef中導入不可變的。

//Mapn//default is hash map ,also provide tree mapnval m = Map("hisung"->90,"wen"->88)//這裡是伴生對象nn//新建一個空map nval m1= new HashMap[String ,Int]n//簡潔的: nscala> val m2= Map[String,Int]()//這是通過伴生對象實現的,和上面那個有本質區別。n//遍歷:nfor((k,v) <- mapp) expressionnn//tuplenmap只能是二元組,tuple則不受限制nval t = (1,"string",3.14) //雙引號n//訪問tuple是從1開始的,這是因為haskell中的習慣 t._2 n//zip函數 ntscala> val s = Array("<",">")nts: Array[String] = Array(<, >)ntntscala> val c = Array(11,12)ntc: Array[Int] = Array(11, 12)ntntscala> s.zip(c)ntres5: Array[(String, Int)] = Array((<,11), (>,12))n

  • 控制結構和模式匹配
  • class

  • trait

  • case class

Scala Case Classes In Depth

  • 模式匹配

  • 隱式轉換
  • 動手寫個dsl
  • 類型系統

推薦閱讀:

我的第一個scala程序
現在有沒有公司使用 Scala 進行 Android 開發,如果有那麼使用哪些工具呢?
scala ++?

TAG:Scala |