scala语言

关于

学习scala后,发现scala就是灵活版的java,他通过引入函数式编程的一些概念来达到这个目的,
并且由于基于JVM,能够复用所有的java库!!如果你嫌java臃肿,不妨试试scala。

基础语法

for(i <- 0 to 100){
    println(i)
}
def addOne(m: Int): Int = m + 1
def adder(m:Int, n:Int) = m + n
val add2 = adder(2, _:Int)
def muliply(m: Int) (n: Int) = m * n
def cap(args String*) = {
    args.map {
        arg => arg.capitalize
    }
}
class Calculator(brand: String) {
  /**
   * A constructor.
   */
  val color: String = if (brand == "TI") {
    "blue"
  } else if (brand == "HP") {
    "black"
  } else {
    "white"
  }

  // An instance method.
  def add(m: Int, n: Int): Int = m + n
}
class ScientificCalculator(brand: String) extends Calculator(brand) {
  def log(m: Double, base: Double) = math.log(m) / math.log(base)
}
abstract class Shape {
        def getArea():Int    // subclass should define this
}
trait Cache[K, V] {
  def get(key: K): V
  def put(key: K, value: V)
  def delete(key: K)
}
def remove[K](key: K)

如何实现像java那样的父类占位符。

class Bar {
    def apply() = 0
}
val bar = new Bar
bar()
// res: Int = 0
object Timer {
  var count = 0

  def currentCount(): Long = {
    count += 1
    count
  }
}
Timer.currentCount()
object addOne extends Function1[Int, Int] {
    def apply(m: Int): Int = m + 1
}
class AddOne extends (Int => Int) {
  def apply(m: Int): Int = m + 1
}
val times = 1

times match {
    case 1 => "one"
    case 2 => "two"
    case _ => "some others"
}

// 守卫匹配
times match {
    case i if i == 1 => "one"
    case i if i == 2 => "two"
    case _ => "some others"
}

匹配类型

def bigger(o: Any): Any = {
  o match {
    case i: Int if i < 0 => i - 1
    case i: Int => i + 1
    case d: Double if d < 0.0 => d - 0.1
    case d: Double => d + 0.1
    case text: String => text + "s"
  }
}

匹配类成员

def calcType(calc: Calculator) = calc match {
  case _ if calc.brand == "hp" && calc.model == "20B" => "financial"
  case _ if calc.brand == "hp" && calc.model == "48G" => "scientific"
  case _ if calc.brand == "hp" && calc.model == "30B" => "business"
  case _ => "unknown"
}
case class Calculator(brand: String, model: String)

case classes are designed to be used with pattern matching. Let’s simplify our calculator classifier example from earlier.
样本类就是被设计用在模式匹配中的。让我们简化之前的计算器分类器的例子。

def calcType(calc: Calculator) = calc match {
  case Calculator("hp", "20B") => "financial"
  case Calculator("hp", "48G") => "scientific"
  case Calculator("hp", "30B") => "business"
  case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel)
}

我们也可以将匹配的值重新命名。

  case c@Calculator(_, _) => "Calculator: %s of unknown type".format(c)

基本数据结构

val numbers = List(1,2,3,4)
scala> Set(1,2,1)
res0: scala.collection.immutable.Set[Int] = Set(1, 2)
val hostPort = ("localhost", 80)
hostPort._1  // localhost
hostPort._2  // 80

与样本类不同,元组不能通过名称获取字段,而是使用位置下标来读取对象;而且这个下标基于1,而不是基于0。
在创建两个元素的元组时,可以使用特殊语法:1 -> 2,见映射

unpack:val (v1, v2) = (1,2), 函数参数unpack例子
_*
def hello( names: String*) {
  println( "Hello " + names.mkString(" and " ) )
}

scala> val names = List("john", "paul", "george", "ringo")
names: List[String] = List(john, paul, george, ringo)
scala> hello( names: _* )
Hello john and paul and george and ringo
Map(1 -> 2)  // 值映射
Map("foo" -> "bar")  // 字符串映射
Map(1 -> Map("foo" -> "bar"))  // 映射到映射
Map("timesTwo" -> {timesTwo(_)})   // 映射到函数

Map中要获取键对应的值,需要使用Map.get方法。
- 选项 Option
Option 是一个表示有可能包含值的容器。
Option 本身是泛型的,有两个子类 Some[T]None
在模式匹配中会用到。

val result = res1 match {
    case Some(n) => n*2
    case None => 0
}

Option基本的接口是这样的:

trait Option[T] {
  def isDefined: Boolean
  def get: T
  def getOrElse(t: T): T
}

Option本身是泛型的,并且有两个子类: Some[T] 或 None。
Map.get使用Option作为其返回值,表示这个方法也许不会
返回你请求的值。
类似于Haskell的Maybe

函数组合子 Functional Combinators

numbers.map((i:Int) => i * 2)
numbers.foreach((i:Int) => i * 2)
List(1,2,3).zip(List("a","b","c"))  //[(1,a),(2,b),(3,c)]

函数组合

val fg = f _ compose g _

println是啥?为甚不能组合。
- andThen,与compose很像,只是执行顺序相反,先执行第一个。

偏函数

不是部分应用函数,篇函数是指只能接受该类型的某些特定的值。
isDefinedAt用来确定该函数能否接受一个给定的参数。

val one: PartialFunction[Int, String] = { case 1 => "one" }
one.isDefinedAt(1)   // true
one.isDefinedAt(2)   // false

PartialFunctions可以使用orElse组成新的函数,得到的PartialFunction反映了是否对给定参数进行了定义。

scala> val two: PartialFunction[Int, String] = { case 2 => "two" }
two: PartialFunction[Int,String] = <function1>

scala> val three: PartialFunction[Int, String] = { case 3 => "three" }
three: PartialFunction[Int,String] = <function1>

scala> val wildcard: PartialFunction[Int, String] = { case _ => "something else" }
wildcard: PartialFunction[Int,String] = <function1>

scala> val partial = one orElse two orElse three orElse wildcard
partial: PartialFunction[Int,String] = <function1>

scala> partial(5)
res24: String = something else

scala> partial(3)
res25: String = three

scala> partial(2)
res26: String = two

scala> partial(1)
res27: String = one

scala> partial(0)
res28: String = something else

模式匹配其实是一个偏函数!偏函数是函数的子类,所以所有在使用函数的地方都可以使用偏函数,即模式匹配!

类型,静态类型

随着类型系统表达能力的提高,我们可以生产更可靠的代码。
所有的类型信息会在编译时被删去,因为它已不再需要。这就是所谓的擦除。

def foo[A, B](f: A->List[A], b: B) = f(b)
def foo[A](f: A->List[A], b: Int) = f(i)
def id[T](x : T) = x
val x = id("hey")

逆变的例子,函数特质。参数用父类,调用用子类,表明以父类为类型参数的函数
是以子类为类型参数的函数的子类。有点绕,理解一下。

scala> def cacophony[T](things: Seq[T]) = things map (_.sound)
<console>:7: error: value sound is not a member of type parameter T
       def cacophony[T](things: Seq[T]) = things map (_.sound)
                                                        ^

scala> def biophony[T <: Animal](things: Seq[T]) = things map (_.sound)
biophony: [T <: Animal](things: Seq[T])Seq[java.lang.String]

scala> biophony(Seq(new Chicken, new Bird))
res5: Seq[java.lang.String] = List(cluck, call)

T :> SomeType 指定T是SomeType的超类。
List 同样 定义了::[B >: T](x: B) 来返回一个List[B],例如下面这个例子中,
flock是Bird类型,Bird是Animal的子类。::操作后返回的是超类Animal的列表。

scala> new Animal :: flock
res59: List[Animal] = List(Animal@11f8d3a8, Bird@7e1ec70e, Bird@169ea8d2)

可以为通配符指定边界。

def count[A](l: List[A]) = l.size
def count(l: List[_]) = l.size

def hashcodes(l: Seq[_ <: AnyRef]) = l map (_.hashCode)
class Container[A <% Int] { def addIt(x: A) = 123 + x }
def assert(assertion: Boolean) {
  if (!assertion)
    throw new java.lang.AssertionError("assertion failed")
}

def assume(assumption: Boolean) {
  if (!assumption)
    throw new java.lang.AssertionError("assumption failed")
}

def require(requirement: Boolean) {
  if (!requirement)
    throw new IllegalArgumentException("requirement failed")
}

隐式转换

隐式函数

implicit def intToString(x:Int) : x.toString

隐式类 2.10.+

隐式类的主方法可以用于隐式类型转换。

object Helpers {
  implicit class IntWithTimes(x: Int) {
    def times[A](f: => A): Unit = {
      def loop(current: Int): Unit =
        if(current > 0) {
          f
          loop(current - 1)
        }
      loop(x)
    }
  }
}

import Helpers._
5 times println("HI")

HI
HI
HI
HI
HI

可以利用 Scala.math 库 Numeric 对数字类型变量进行限制

构建工具 SBT

字符串 - 核心数据结构

scala的字符串很多是直接借助于java的String类,但是还有一些scala的特性需要说明一下。

字符串插值 String Interpolation,Scala 2.10.+

scala提供三种字符串插值方法,s, f and raw。
- s支持局部变量和表达式。
- f表明对变量进行格式化,类似于printf的功能。类型安全,如果不匹配,将会报错。%s是通用的?!
- raw字符串就是不会对转义字符转义。相当于python里面的r

// 插入局部变量
val name = "James"
println(s"Hello, $name")  // Hello, James
// 插入表达式
println(s"1 + 1 = ${1 + 1}")

val height = 1.9d
val name = "James"
// f 使用类似于printf格式字符
println(f"$name%s is $height%2.2f meters tall")  // James is 1.90 meters tall

println(raw"a\nb") // Output: a\nb

集合 - 核心数据结构

List

List(1,2,3,4)
1 :: 2 :: 3 :: Nil

val L = 1 to 1000 toList

::是将前面的数据prepend到后面的列表中,等同于+:

Set

Seq

貌似与list没啥区别,需要再仔细看看。

scala> Seq(1, 1, 2)
res3: Seq[Int] = List(1, 1, 2)

请注意返回的是一个列表。因为Seq是一个特质;而列表是序列的很好实现。

".mkString(seq)" 方法可以实现python的join方法的功能。

Map

Map('a' -> 1, 'b' -> 2)

层次结构

def hasNext(): Boolean
def next(): A
def contains(key: A): Boolean
def +(elem: A): Set[A]
def -(elem: A): Set[A]

常用子类

val r0 = 0 until 10
val r1 = 0 until 10 by 2
new Range(start: Int, end: Int, step: Int)

一些描述特性的特质

可变 vs 不可变

不可变

优点

在多线程中不会改变
缺点

一点也不能改变
Scala允许我们是务实的,它鼓励不变性,但不惩罚我们需要的可变性。这和var vs. val非常相似。我们总是先从val开始并在必要时回退为var。

我们赞成使用不可改变的版本的集合,但如果性能使然,也可以切换到可变的。使用不可变集合意味着你在多线程不会意外地改变事物。

可变集合

与Java转换

您可以通过JavaConverters package轻松地在Java和Scala的集合类型之间转换。它用asScala 装饰常用的Java集合以和用asJava 方法装饰Scala集合。

   import scala.collection.JavaConverters._
   val sl = new scala.collection.mutable.ListBuffer[Int]
   val jl : java.util.List[Int] = sl.asJava
   val sl2 : scala.collection.mutable.Buffer[Int] = jl.asScala
   assert(sl eq sl2)

双向转换:

scala.collection.Iterable <=> java.lang.Iterable
scala.collection.Iterable <=> java.util.Collection
scala.collection.Iterator <=> java.util.{ Iterator, Enumeration }
scala.collection.mutable.Buffer <=> java.util.List
scala.collection.mutable.Set <=> java.util.Set
scala.collection.mutable.Map <=> java.util.{ Map, Dictionary }
scala.collection.mutable.ConcurrentMap <=> java.util.concurrent.ConcurrentMap

此外,也提供了以下单向转换

scala.collection.Seq => java.util.List
scala.collection.mutable.Seq => java.util.List
scala.collection.Set => java.util.Set
scala.collection.Map => java.util.Map

使用specs进行测试

貌似现在包为specs2,导入单元测试规范org.specs2.mutable.Specification

org.specs2.mutable._

object ArithmeticSpec extends Specification {
  "Arithmetic" should {
    "add two numbers" in {
      1 + 1 mustEqual 2
    }
    "add three numbers" in {
      1 + 1 + 1 mustEqual 3
    }
  }
}

并发编程*

trait Runnable {
  def run(): Unit
}

trait Callable[V] {
  def call(): V
}
val hello = new Thread(new Runnable {
  def run() {
    println("hello world")
  }
})

hello.start()

创建一个自己的线程步骤,首先创建一个实现Runnable接口的类,然后将该类的实例作为参数传给new Thread()即可,
然后再调用创建的线程的.start()方法就可以运行了。

也可以利用java的线程的执行服务构建一个线程池。java.util.concurrent.{Executors, ExecutorService}

val pool: ExecutorService = Executors.newFixedThreadPool(poolSize)
pool.execute(new MyRunableClass())

线程安全的三种工具。

// synchronized
class Person(var name: String) {
  def set(changedName: String) {
    this.synchronized {
      name = changedName
    }
  }
}

// volatile
class Person(@volatile var name: String) {
  def set(changedName: String) {
    name = changedName
  }
}

// AtomicReference
import java.util.concurrent.atomic.AtomicReference

class Person(val name: AtomicReference[String]) {
  def set(changedName: String) {
    name.set(changedName)
  }
}

Java跨平台交互:在Java中使用Scala

扩展规格

泛型编程

从classTag创建对象的方法还没搞清楚,参看代码!:

import scala.reflect.{classTag, ClassTag}

def objectFile[T: ClassTag](
      path: String,
      minPartitions: Int = defaultMinPartitions): RDD[T] = withScope {
    assertNotStopped()
    sequenceFile(path, classOf[NullWritable], classOf[BytesWritable], minPartitions)
      .flatMap(x => Utils.deserialize[Array[T]](x._2.getBytes, Utils.getContextOrSparkClassLoader))
  }


private implicit def arrayToArrayWritable[T <% Writable: ClassTag](arr: Traversable[T])
  : ArrayWritable = {
  def anyToWritable[U <% Writable](u: U): Writable = u

  new ArrayWritable(classTag[T].runtimeClass.asInstanceOf[Class[Writable]],
      arr.map(x => anyToWritable(x)).toArray)
}

Test

http://www.scalatest.org/

build.sbt 引入测试包

libraryDependencies += "org.scalatest" % "scalatest_2.10" % "2.2.6" % "test"

FlatSpec

"X should Y," "A must B,"

Achieving success

FunSuit

import org.scalatest.FunSuite

class SetSuite extends FunSuite {

  test("An empty Set should have size 0") {
    assert(Set.empty.size == 0)
  }

  test("Invoking head on an empty Set should produce NoSuchElementException") {
    assertThrows[NoSuchElementException] {
      Set.empty.head
    }
  }
}

现在用的是3.0, intellij 好像支持得不好。最好还是用 2.2.6吧!