Scala學習——Brief Scala Tutorial

由於Spark項目須要,學習Scala編程。html

從官網文檔入手:http://www.scala-lang.org/documentation/java

首先從他的Older Documentation入手。程序員

第一篇是Brief Scala Tutorialexpress

只有20頁,對於有Java基礎的人來講上手很快編程

其中有幾點值得注意數據結構

(1)函數式編程

object Timer {
  def oncePerSecond(callback: ()
=> Unit) {     while (true) { callback(); Thread sleep 1000 }
  }
  def timeFlies() {     println(
"time flies like an arrow...")   }
  def main(args: Array[String]) { oncePerSecond(timeFlies)}
}

官網給出的解釋是ide

The type of this function is written () => Unit and is the type of all functions which take no arguments and return nothing (the type Unit is similar to void in C/C++).  函數式編程

這段不知道怎麼翻譯合適。函數

 

(2)匿名方法post

object TimerAnonymous {
  def oncePerSecond(callback: () => Unit) {
    while (true) { callback(); Thread sleep 1000 } }
  def main(args: Array[String]) { oncePerSecond(() =>
    println("time flies like an arrow...")) }
}

用匿名方法的話編程更簡單直白,在main方法中直接用=>分開了方法名參數及方法體。和上面的例子效果是相同的。

 

(3)Scala的class能夠帶參數:

class Complex(real: Double, imaginary: Double) {
    def re() = real
    def im() = imaginary 
}

這樣的語句定義了一個單例對象:一個有且僅有一個實例的類。object語句在定義了一個叫HelloWorld的類的同時還定義了一個叫HelloWorld的實例。這個實例在第一次使用的時候會進行實例化。聰明的讀者可能會發現main函數並無使用static修飾符,這是因爲靜態成員(方法或者變量)在Scala中並不存在。Scala從不定義靜態成員,而經過定義單例object取而代之。

在def re()=real中沒有指定返回值類型,而是由編譯器根據「=」右邊的類型來判斷該方法的返回值類型。固然編譯器不是總能推斷出正確的變量類型,這就須要編程人員在編程初期嘗試去掉變量類型定義,若是編譯器不報錯的話就表示他能夠推斷類型,若是報錯就仍是老老實實地把類型加上。等到編的多了就知道什麼能夠什麼不能夠了。

固然,這裏的re()和im()的括號是能夠去掉的。

class Complex(real: Double, imaginary: Double) { 
  def re = real   def im = imaginary }

 

(4)繼承與重寫

class Complex(real: Double, imaginary: Double) { 
  def re = real   def im = imaginary   override def toString() =     "" + re + (if (im < 0) "" else "+") + im + "i"
}

若是子類的方法重寫了父類的方法,必需要有override修飾語

若是一個類沒有繼承任何類的話,隱式繼承scala.AnyRef 

 

(5)case class

 對於處理樹結構的數據很適用

abstract class Tree
case class Sum(l: Tree, r: Tree) extends Tree 
case class Var(n: String) extends Tree case class Const(v: Int) extends Tree

以上的Sum,Var和Const就是case class,與標準的class有如下幾個區別

  • 不用new原語去實例化(e.g.能夠直接寫成Const(5),而不用new Const(5))
  • case類的構造器參數被看成公開值並能夠直接被訪問 e.g. 
    val x = Var("x")  
    Console.println(x.name) 
  • 已提供方法equals和hashCode的默認定義,work on the structure of the instances and not on their identity(英文水平讓人抓狂啊)這裏的instance structureidentity是個啥
  • 提供toString()默認定義,打印值的source form。e.g.the tree for expression x+1 prints as Sum(Var(x),Const(1)) 
  • 分解數據結構的時候用到了模式匹配

模式匹配的東西有點難,

def eval(t: Tree, env: Environment): Int = t match {
  case Sum(l, r) => eval(l, env) + eval(r, env) 
  case Var(n) => env(n)   case Const(v) => v }
Environment的目的就是爲了給變量賦值

一個熟練的面向對象的程序員可能想知道爲何咱們不吧eval定義爲Tree或者其之類的成員函數。咱們事實上能夠這麼作。由於Scala容許條件類象普通類那樣定義成員。決定是否使用模式匹配或者成員函數取決於程序員的喜愛,不過這個取捨還和可擴展性有重要聯繫:
當你使用成員函數時,你能夠經過繼承Tree從而很容易的添加新的節點類型,可是另一方面,添加新的操做也是很繁雜的工做,由於你不得不修改Tree的全部子類。
當你使用模式匹配是,形勢正好逆轉過來,添加新的節點類型要求你修改全部的對樹使用模式匹配的函數,可是另外一方面,添加一個新的操做只須要再添加一個模式匹配函數就能夠了。

下面是模式匹配裏的對符號求導數

首先看下導數的性質:

  • 和的導數就是導數的和
  • 若是符號等以求導的符號,則導數爲1,不然爲0
  • 常量的導數是0
def derive(t: Tree, v: String): Tree = t match { 
  case Sum(l, r) => Sum(derive(l, v), derive(r, v)) 
  case Var(n) if (v == n) => Const(1)
  case _ => Const(0)
}
大概的意思是首先他會檢查t是否是一個sum,若是fail了就檢查是否是var,再fail檢查是否是一個Const。
仍是來個實際例子看一下:
def main(args: Array[String]) {
  val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) 
  val env: Environment = { case "x" => 5 case "y" => 7 }
  println("Expression: " + exp)   println("Evaluation with x=5, y=7: " + eval(exp, env))
  println("Derivative relative to x:\n " + derive(exp, "x"))
  println("Derivative relative to y:\n " + derive(exp, "y")) }
輸出:
Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y)))
Evaluation with x=5, y=7: 24
Derivative relative to x:Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0)))
Derivative relative to y:Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1)))

 

(6)traits

Scala的traits和Java中的interface相似,但有別於interface的是traits能夠實現部分方法,而且能夠繼承多個。

Tutorial裏的例子不是很直觀,因而摘了網上的一個例子[1]

abstract class Animal {
  def walk(speed:Int)

  def breathe() = {
    println("animal breathes")
  }
}

這裏的抽象類Animal定義了walk方法,實現了breathe方法。

再看下Flyable和Swimable兩個trait的實現:

trait Flyable {
  def hasFeather = true
  def fly
}
trait Swimable { def swim }

注意Flyable trait中有兩個方法,一個是hasFeather方法,這個方法已經實現了,另外一個方法是fly方法,這個方法只是定義沒有實現,而Swimable trait只是定義個一個swim的方法,沒有具體實現。

下面咱們定義一種動物,它既會飛也會游泳,這種動物是魚鷹 FishEagle,咱們看下代碼:

class FishEagle extends Animal with Flyable with Swimable {
  def walk(speed:Int) = println("fish eagle walk with speed " + speed)
  def swim() = println("fish eagle swim fast")
  def fly() = println("fish eagle fly fast")
}

在類的實現中須要實現抽象類Animal的walk方法,也須要實現兩個特徵中定義的未實現方法。

下面main方法代碼:

object App { 
 def main(args : Array[String]) { 
 val fishEagle = new FishEagle 
 val flyable:Flyable = fishEagle 
 flyable.fly 
 
 val swimmer:Swimable = fishEagle 
 swimmer.swim 
 } 
}

 在main方法中,咱們首先初始化了一個FishEagle對象,而後經過Flyable和Swimable trait來分別調用其fly和swim方法,輸出結果以下:

fish eagle fly fast 
fish eagle swim fast

trait的使用方法就是這樣子了,它很強大,抽象類能作的事情,trait均可以作。它的長處在於能夠多繼承。

trait和抽象類的區別在於抽象類是對一個繼承鏈的,類和類以前確實有父子類的繼承關係,而trait則如其名字,表示一種特徵,能夠多繼承。

 還有一個特色就是with用關鍵字with能夠混入更多的trait。若是類已經繼承了類,就可使用with混入trait。

class Animal
class Dog(val name: String) extends Animal with Friend{}

咱們還能夠在實例一級進行混入,這樣的話就能夠把特定的類的實例當作trait

class Cat(val name: String) extends Animal
val  scat  = new Cat("cat") with Friend
//這樣scat就是Friend了

 

 

(7)泛型

相關文章
相關標籤/搜索