[关闭]
@MiloXia 2014-12-08T10:56:31.000000Z 字数 8649 阅读 3044

Scala 学习笔记

  • Option
    它是一个具有两个子类 Some[T] 和 None 的泛型类,用来表示 “无值” 的可能性None is a singleton (like nil), Some 是一个实际结果的容器Some(result)
    是为解决Java中无意义的null
    trait Option[T] {
    def isDefined: Boolean
    def get: T
    def getOrElse(t: T): T
    }
  1. def find[A, B](it: Iterator[(A, B)], x: A): Option[B] = {
  2. var result: Option[B] = None //不要是null
  3. while (it.hasNext && result == None) {
  4. val Pair(x1, y) = it.next
  5. if (x == x1) result = Some(y)
  6. }
  7. result
  8. }
  9. Option(null) //==> None //对空值包装比较好Option(getReward())为空时就为None
  10. Some(null) //==> Some(null)
  11. Some(1,2,3) == Option(1,2,3) == Some((1,2,3)) //内部是元组
  12. None getOrElse 0 //==> 0
  13. Some(1) getOrElse 0 //==> 1
  14. /*
  15. * Option 实现了 map, flatMap, and filter 接口
  16. * (这样 Scala编译器就允许在 'for'循环里使用它了...)
  17. */
  18. (for (val user <- User.find(id)) //可能返回None
  19. yield {
  20. process(user)
  21. }) getOrElse {error("...")}
  • PartialFunction (偏函数)
    trait PartialFunction[-A, +B] extends (A) => B
    该语句表明偏函数:
    1) 是scala中的一个源于A转换为B的特质(trait),
    2) 是一个一元函数,定义域为类型-A,-表明仅仅是A或A的父类,具有“部分”的含义
    3) 函数有可能“偏”离了A定义域(Type)类型,而成为值域B, +表明可以是B或B的子类,具有“全部”的含义。
  1. /*
  2. * 在Scala中,被“{}”包含的一系列case语句可以被看成是一个函数字面量,
  3. * 它可以被用在任何普通的函数字面量适用的地方,例如被当做参数传递
  4. */
  5. List(1,2,3,4) map {case 1 => 6; case _ => 0}
  6. //==> List(6,0,0,0)
  7. //pf是一个函数字面量,它的值是 {case 1 => "a";case 2 => "b"}
  8. val pf:PartialFunction[Int, String] = {case 1 => "a";case 2 => "b"}
  9. pf(1) //==> "a"
  10. pf(3) //==> error
  11. //就不难理解在Scala的Actor中经常使用的react函数的语法形式
  12. /*
  13. * 如果一一组case语句没有涵盖所有的情况,那么这组case语句就可以被看做是
  14. * 一个偏函数。
  15. */
  16. val second:PartialFunction[List[Int],Int] = {
  17. case List(x::y::_) => y //忽略Nil和长度为1的列表
  18. }
  19. second(List(2)) //==> error:scala.MatchError
  20. //调用函数前先检查一个参数是否在定义域范围以避免抛出异常
  21. second.isDefinedAt(List(2,3,4)) ==> true
  22. //实际上,scala编译器把函数字面量: {case List(x::y::_) => y}
  23. //编译成了如下的等价形式:
  24. new PartialFunction[List[Int], Int] {
  25. def apply(xs: List[Int]) = xs match {
  26. case x :: y :: _ => y
  27. }
  28. def isDefinedAt(xs: List[Int]) = xs match {
  29. case x :: y :: _ => true
  30. case _ => false
  31. }
  32. }
  33. //Tips:一组case语句要成为一个偏函数,那么它被赋予的变量必须被声明为PartionFunction[-A,+B]
  34. /*实际应用*/
  35. //1)
  36. val pf:PartialFunction[Int, String] = {case i if i % 2 == 0 => "even"}
  37. val tf = pf orElse {case _ => "odd"} //又一个偏函数
  38. tf(1) //==> "odd"
  39. tf(2) //==> "even"
  40. //2) 方法参数
  41. trait Act[T] {
  42. def react(f:PartialFunction[T, Unit])
  43. //和 def react(f1:T => Unit) 不一样: f1 范围更大,f只能传入{case.}
  44. // f 可以用orElse {case.} 组合, f1不可以
  45. //Int => Unit != PartialFunction[Int,Unit]
  46. }
  47. val act:Act[Int] = ...
  48. act.react {
  49. case 1 => ...
  50. case 2 => ...
  51. }
  52. //3) 返回Option时
  53. Any => Option[Any] /*改成:*/ PartialFunction[Any, Option[Any]]
  54. //这样更具组合性
  55. val f1:PartialFunction[Int, Option[Int]] = {case 1 => Some(1)}
  56. val f2:PartialFunction[Int, Option[Int]] = {case 2 => Some(2)}
  57. val f3 = f1 orElse f2 orElse {case _ => None}
  58. f3(2) //==> Some(2)
  59. f3(3) //==> None
  60. //4) LiftView 的dispatch方法
  61. def dispatch:PartialFunction[String, ()=>NodeSeq] = {case _ => ()=>""}
  62. override def dispatch = {case "url" => doSomething _}
  • flatMap & map
    flatMap函数可以将一条记录转换成多条记录(一对多关系)
    flatMap是一个常用的combinator,它结合了map和flatten的功能。flatMap接收一个可以处理嵌套列表的函数,然后把返回结果连接起来。
    map函数将一条记录转换为另一条记录(一对一关系)
    flatten可以把嵌套的结构展开。
  1. //flatten 展开列表
  2. List(List(1, 2), List(3, 4)).flatten
  3. //==> List(1, 2, 3, 4)
  4. //flatMap 展开列表
  5. List(List(1,2),List(3,4)).flatMap{x => x}
  6. //==> List(1, 2, 3, 4)
  7. //flatMap map和flat列表
  8. List(List(1,2),List(3,4)).flatMap{x => x map{ y => y * 2} }
  9. //==> List(2, 4, 6, 8)
  10. List(List(1,2),List(3,4)).map{x => x map{ y => y * 2} }
  11. //==> List(List(2, 4), List(6, 8))
  12. //map+flatten 组合
  13. List(List(1,2),List(3,4)).map{x => x map{ y => y * 2} }.flatten
  14. //==> List(2, 4, 6, 8)
  15. /等效for
  16. for(x <- List(List(1,2),List(3,4)); y <-x) yield {2 * y}
  17. //==> List(2, 4, 6, 8)
  • 函数
  1. /*
  2. * 1) 匿名函数
  3. */
  4. (x: Int) => x + 1 == (x: Int) => {x + 1} == {x: Int => x + 1}
  5. //==> true
  6. val f = (x: Int) => x + 1 //函数字面量
  7. //在将一个匿名函数作为参数进行传递时,这个语法会经常被用到。
  8. List(1,2,3) map {a => a + 1}
  9. /*
  10. * 2) 部分应用函数 和柯理化差不多
  11. */
  12. val f = (x:Int, y:Int) => x + y
  13. val fp = f _
  14. fp()(1,2) //==> 3
  15. val fq = f(1, _:Int)
  16. fp(2) == fp.apply(2) //=> 3
  17. /*
  18. * 3) 柯里化函数
  19. */
  20. def multiply(m: Int)(n: Int): Int = m * n
  21. multiply(2)(3) //==> 6
  22. //multiply(2) 返回 (n: Int) => 2 * n 这样的函数
  23. //你可以填上第一个参数并且部分应用第二个参数
  24. val timesTwo = multiply(2) _
  25. timesTwo(3) //==> 6
  26. //借贷模式
  27. def using(file:File)(op:PrintWriter => Unit) {
  28. val writer = new PrintWriter(file)
  29. try {
  30. op(writer)
  31. } finally {
  32. writer.close()
  33. }
  34. }
  35. using(new File("data.txt")) {
  36. writer => writer.println("data");
  37. }
  38. //可以对任何多参数函数(不可以是字面量)执行柯里化
  39. def add(x:Int, y:Int):Int = x+y
  40. val a = (add _).curried
  41. a(1)(2) //==> 3
  42. //变长参数
  43. def capitalizeAll(args: String*) = {
  44. args.map { arg =>
  45. arg.capitalize
  46. }
  47. }
  • 函数组合
    1. compose 和 andThen 可以组合任意 部分应用函数
  1. def f(s: String) = "f(" + s + ")"
  2. def g(s: String) = "g(" + s + ")"
  3. val fComposeG = f _ compose g _
  4. fComposeG("yay") //==> f(g(yay))
  5. //andThen为反序
  6. val fAndThenG = f _ andThen g _
  7. fAndThenG("yay") //==> g(f(yay))
  8. // 或者用偏函数orElse来组合
  9. val two: PartialFunction[Int, String] = { case 2 => "two" }
  10. val three: PartialFunction[Int, String] = { case 3 => "three" }
  11. val partial = two orElse three
  12. partial(2) //==> two
  1. class Calculator {
  2. val brand: String = "HP" //不写访问修饰符 默认为public
  3. //会为public的 val 字段生成getter 函数 def brand:String = this.brand
  4. //java中允许字段和方法同名 scala不允许 def 方法可被 val字段重写
  5. //会为public的 var 字段生成 getter & setter函数 brand & brand:=
  6. def add(m: Int, n: Int): Int = m + n
  7. }
  8. val calc = new Calculator
  9. calc.brand //==> HP
  10. /*
  11. * 1) 构造函数
  12. */
  13. class Calculator(brand: String) {
  14. require(brand != "") //参数验证
  15. /**
  16. * A constructor.
  17. */
  18. val color:String = brand match {
  19. case "TI" => "blue"
  20. case "HP" => "black"
  21. case _ => "white"
  22. }
  23. //副构造器
  24. def this() = this("HP")
  25. // An instance method.
  26. def add(m: Int, n: Int): Int = m + n
  27. }
  28. /*
  29. * 2) 参数化字段
  30. */
  31. class Calculator(val color: String) {..}
  32. //等效
  33. class Calculator(brand: String) {
  34. val color: String = brand //去除一次累赘赋值
  35. //color的gtter
  36. ..
  37. }
  38. //也可重写字段
  39. class SubCalculator (
  40. override val color: String
  41. ) extends Calculator(color) {..}
  42. val sc = new SubCalculator("yellow");
  43. sc.color //==> "yellow"
  44. /*
  45. * 3) 函数和方法的区别
  46. */
  47. class C {
  48. var acc = 0
  49. def minc = { acc += 1 }
  50. val finc = { () => acc += 1 }
  51. }
  52. val c = new C
  53. c.minc //c.acc = 1
  54. c.finc //==> () => Unit 返回一个函数
  55. c.finc() //c.acc = 2 执行返回的函数
  56. val f = c.finc
  57. f() //c.acc = 3 字段acc被闭包给带出了实例外 可怕的能力
  58. /*
  59. * 4) 抽象类
  60. */
  61. abstract class Shape {
  62. def getArea():Int // 方法声明 交给子类实现
  63. val width:Int // 字段声明 交给子类实现 与Java不一样
  64. val height:Int // 如果想一样就这样声明 val width:Int = _
  65. } //这么看起来Shape目前更像java接口
  66. //***不可实例化 但可以 实例化匿名子类
  67. val s = new Shape {
  68. def getArea():Int = width * height
  69. val width:Int = 1
  70. val height:Int = 2
  71. }
  72. s.getArea() //==> 2
  • Traits 特质
    特质是一些字段和行为的集合,可以扩展或混入(mixin)你的类中
    与Ruby模块类似 但是trait更有特色
    一个类可以混入多个特质 特质可以叠加
  1. trait Car {
  2. val brand: String
  3. }
  4. class BMW extends Car {
  5. val brand = "BMW"
  6. }
  7. //或 对象也可混入特质 **让对象瞬间拥有某能力 比猴子补丁安全些
  8. class BMW
  9. val b:Car = new BMW with Car {
  10. val brand = "BMW"
  11. }
  12. //或 实例化 trait
  13. val c:Car = new Car {
  14. val brand = "抵制日货"
  15. }
  16. //何时用特质和抽象类?
  17. //a. 优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类
  18. //b. 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。
  19. //特质可以叠加
  20. //特质里面的super调用是动态绑定的
  21. abstract class Base {
  22. def call(x:Int):Int
  23. }
  24. class SubB extends Base { //普通类只对参数安全加1
  25. def call(x:Int) = this.synchronized {
  26. x + 1
  27. }
  28. }
  29. trait Doubling extends Base { //*只能被Base子类混入
  30. abstract override def call(x:Int) = { //必须这样声明方法
  31. super.call(2 * x)
  32. }
  33. }
  34. trait Filtering extends Base { //*只能被Base子类混入
  35. abstract override def call(x:Int) = {
  36. if(x >= 0) super.call(x) else x
  37. }
  38. }
  39. new SubB with Doubling with Filtering call -1
  40. //==> -1 先执行 Filtering的方法 直接返回
  41. new SubB with Doubling with Filtering call 1
  42. //==> 3 一次 filtering、doubling、和subB的方法
  43. new SubB with Filtering with Doubling call -1
  44. //==> -2 先doubling 后被 filtering 过滤
  • object 单例对象
    单例对象用于持有一个类的唯一实例。通常用于工厂模式。
    原静态方法和字段都定义在单例对象中。
    单例对象可以和类具有相同的名称,此时该对象也被称为“伴生对象”。我们通常将伴生对象作为工厂使用。
  1. class Bar(foo: String)
  2. object Bar {
  3. def apply(foo: String) = new Bar(foo)
  4. }
  5. var b = Bar() //apply 方法会被调用
  • 函数即对象
    函数是一些特质的集合。具体来说,具有一个参数的函数是Function1特质的一个实例。这个特征定义了apply()语法糖,让你调用一个对象时就像你在调用一个函数。
  1. class AddOne extends Function1[Int, Int] {
  2. def apply(m: Int): Int = m + 1
  3. }
  4. val plusOne = new AddOne()
  5. plusOne(1) //==> 2 apply被调用
  6. //或者
  7. object addOne extends Function1[Int, Int] {
  8. def apply(m: Int): Int = m + 1
  9. }
  10. addOne(1) //==> 2

这个Function特质集合下标从0开始一直到22。为什么是22?这是一个主观的魔幻数字(magic number)。我从来没有使用过多于22个参数的函数,所以这个数字似乎是合理的。

apply语法糖有助于统一对象和函数式编程的二重性。你可以传递类,并把它们当做函数使用,而函数本质上是类的实例。
这是否意味着,当你在类中定义一个方法时,得到的实际上是一个Function*的实例?不是的,在类中定义的方法是方法而不是函数。在repl中独立定义的方法是Function*的实例

  1. //可以使用更直观快捷的extends (Int => Int)代替extends Function1[Int, Int]
  2. class AddOne extends (Int => Int) {
  3. def apply(m: Int): Int = m + 1
  4. }
  • 样本类 Case Classes
    使用样本类可以方便得存储和匹配类的内容。你不用new关键字就可以创建它们。
  1. class Calculator(brand: String, model: String)//普通类
  2. case class Calculator(brand: String, model: String)//样本类

于普通类的区别
1.样本类有于类名一样的工厂函数 不需要new 来创建 只需
val hp20B = Calculator("hp", "20b")

2.样本类参数类表中所有参数隐式获得了val前缀
class Calculator(brand: String, model: String) //
a. 非val var 会生成private brand 和 private brand
以及private的getter&setter方法 所以外部访问不到
b. var 会生成私有字段和getter&setter方法
class Calculator(val brand: String)
c. val 会生成 private brand 和 getter方法
样本类隐式获得val前缀

  1. case class Calculator(brand: String, model: String)
  2. val hp20B = Calculator("hp", "20b")
  3. hp20B.brand //==> hp
  4. hp20B.brand = "qq" //==> error val....

3.编译器自动添加了toString、hashCode、equals

用途
样本类主要用于模式匹配

  1. val hp20b = Calculator("hp", "20B")
  2. val hp30b = Calculator("hp", "30B")
  3. def calcType(calc: Calculator) = calc match {
  4. case Calculator("hp", "20B") => "financial"
  5. case Calculator("hp", "48G") => "scientific"
  6. case Calculator("hp", "30B") => "business"
  7. case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel)
  8. }
  9. //最后一个匹配还可以这样写:
  10. case c @ Calculator(_, _) => "Calculator: %s of unknown type".format(c)
  11. //以上为在匹配变量上套用匹配
  12. calcType(Calculator("aa", "2"))
  13. //==> Calculator: Calculator(aa, 2) of unknown type
  • Map 与 Tuple(元组)
  1. Map(1 -> 2)
  2. Map("foo" -> "bar")
  3. // 1->2 为 (1).->(2) 返回 元组(1,2)
  4. Map(1 -> 2) == Map((1,2)) //==> true
  5. //映射的值可以是映射甚或是函数。
  6. Map(1 -> Map("foo" -> "bar"))
  7. def addOne(x:Int) = x + 1
  8. val map = Map("addOne" -> { addOne(_) }) //偏应用函数
  9. map("addOne")(1) //==> 2
  10. //或 函数字面量
  11. val map = Map("addOne" -> { x:Int => x + 1 })
  • Map 与 Option
    map get方法 返回Option[T]类型
  1. val map = Map(1 -> 1, 2 -> 2, 3 -> 3)
  2. map(1) //==> 1
  3. map(4) //==> java.util.NoSuchElementException: key not found: 4
  4. m contains 4 //==> false 这种方式来安全获取
  5. //或用get方法
  6. m get 1 //==> Some(1)
  7. m get 4 //==> None
  8. //针对 Option 处理方式
  9. m get 4 getOrElse 0 //==> 0
  10. m get 4 match {
  11. case Some(n) => n
  12. case None => 0
  13. }
  14. //==> 0
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注