@MiloXia
2014-12-08T10:56:31.000000Z
字数 8649
阅读 3383
- 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
}
def find[A, B](it: Iterator[(A, B)], x: A): Option[B] = {var result: Option[B] = None //不要是nullwhile (it.hasNext && result == None) {val Pair(x1, y) = it.nextif (x == x1) result = Some(y)}result}Option(null) //==> None //对空值包装比较好Option(getReward())为空时就为NoneSome(null) //==> Some(null)Some(1,2,3) == Option(1,2,3) == Some((1,2,3)) //内部是元组None getOrElse 0 //==> 0Some(1) getOrElse 0 //==> 1/** Option 实现了 map, flatMap, and filter 接口* (这样 Scala编译器就允许在 'for'循环里使用它了...)*/(for (val user <- User.find(id)) //可能返回Noneyield {process(user)}) 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的子类,具有“全部”的含义。
/** 在Scala中,被“{}”包含的一系列case语句可以被看成是一个函数字面量,* 它可以被用在任何普通的函数字面量适用的地方,例如被当做参数传递*/List(1,2,3,4) map {case 1 => 6; case _ => 0}//==> List(6,0,0,0)//pf是一个函数字面量,它的值是 {case 1 => "a";case 2 => "b"}val pf:PartialFunction[Int, String] = {case 1 => "a";case 2 => "b"}pf(1) //==> "a"pf(3) //==> error//就不难理解在Scala的Actor中经常使用的react函数的语法形式/** 如果一一组case语句没有涵盖所有的情况,那么这组case语句就可以被看做是* 一个偏函数。*/val second:PartialFunction[List[Int],Int] = {case List(x::y::_) => y //忽略Nil和长度为1的列表}second(List(2)) //==> error:scala.MatchError//调用函数前先检查一个参数是否在定义域范围以避免抛出异常second.isDefinedAt(List(2,3,4)) ==> true//实际上,scala编译器把函数字面量: {case List(x::y::_) => y}//编译成了如下的等价形式:new PartialFunction[List[Int], Int] {def apply(xs: List[Int]) = xs match {case x :: y :: _ => y}def isDefinedAt(xs: List[Int]) = xs match {case x :: y :: _ => truecase _ => false}}//Tips:一组case语句要成为一个偏函数,那么它被赋予的变量必须被声明为PartionFunction[-A,+B]/*实际应用*///1)val pf:PartialFunction[Int, String] = {case i if i % 2 == 0 => "even"}val tf = pf orElse {case _ => "odd"} //又一个偏函数tf(1) //==> "odd"tf(2) //==> "even"//2) 方法参数trait Act[T] {def react(f:PartialFunction[T, Unit])//和 def react(f1:T => Unit) 不一样: f1 范围更大,f只能传入{case.}// f 可以用orElse {case.} 组合, f1不可以//Int => Unit != PartialFunction[Int,Unit]}val act:Act[Int] = ...act.react {case 1 => ...case 2 => ...}//3) 返回Option时Any => Option[Any] /*改成:*/ PartialFunction[Any, Option[Any]]//这样更具组合性val f1:PartialFunction[Int, Option[Int]] = {case 1 => Some(1)}val f2:PartialFunction[Int, Option[Int]] = {case 2 => Some(2)}val f3 = f1 orElse f2 orElse {case _ => None}f3(2) //==> Some(2)f3(3) //==> None//4) LiftView 的dispatch方法def dispatch:PartialFunction[String, ()=>NodeSeq] = {case _ => ()=>""}override def dispatch = {case "url" => doSomething _}
- flatMap & map
flatMap函数可以将一条记录转换成多条记录(一对多关系)
flatMap是一个常用的combinator,它结合了map和flatten的功能。flatMap接收一个可以处理嵌套列表的函数,然后把返回结果连接起来。
map函数将一条记录转换为另一条记录(一对一关系)
flatten可以把嵌套的结构展开。
//flatten 展开列表List(List(1, 2), List(3, 4)).flatten//==> List(1, 2, 3, 4)//flatMap 展开列表List(List(1,2),List(3,4)).flatMap{x => x}//==> List(1, 2, 3, 4)//flatMap map和flat列表List(List(1,2),List(3,4)).flatMap{x => x map{ y => y * 2} }//==> List(2, 4, 6, 8)List(List(1,2),List(3,4)).map{x => x map{ y => y * 2} }//==> List(List(2, 4), List(6, 8))//map+flatten 组合List(List(1,2),List(3,4)).map{x => x map{ y => y * 2} }.flatten//==> List(2, 4, 6, 8)/等效forfor(x <- List(List(1,2),List(3,4)); y <-x) yield {2 * y}//==> List(2, 4, 6, 8)
- 函数
/** 1) 匿名函数*/(x: Int) => x + 1 == (x: Int) => {x + 1} == {x: Int => x + 1}//==> trueval f = (x: Int) => x + 1 //函数字面量//在将一个匿名函数作为参数进行传递时,这个语法会经常被用到。List(1,2,3) map {a => a + 1}/** 2) 部分应用函数 和柯理化差不多*/val f = (x:Int, y:Int) => x + yval fp = f _fp()(1,2) //==> 3val fq = f(1, _:Int)fp(2) == fp.apply(2) //=> 3/** 3) 柯里化函数*/def multiply(m: Int)(n: Int): Int = m * nmultiply(2)(3) //==> 6//multiply(2) 返回 (n: Int) => 2 * n 这样的函数//你可以填上第一个参数并且部分应用第二个参数val timesTwo = multiply(2) _timesTwo(3) //==> 6//借贷模式def using(file:File)(op:PrintWriter => Unit) {val writer = new PrintWriter(file)try {op(writer)} finally {writer.close()}}using(new File("data.txt")) {writer => writer.println("data");}//可以对任何多参数函数(不可以是字面量)执行柯里化def add(x:Int, y:Int):Int = x+yval a = (add _).currieda(1)(2) //==> 3//变长参数def capitalizeAll(args: String*) = {args.map { arg =>arg.capitalize}}
- 函数组合
- compose 和 andThen 可以组合任意 部分应用函数
def f(s: String) = "f(" + s + ")"def g(s: String) = "g(" + s + ")"val fComposeG = f _ compose g _fComposeG("yay") //==> f(g(yay))//andThen为反序val fAndThenG = f _ andThen g _fAndThenG("yay") //==> g(f(yay))// 或者用偏函数orElse来组合val two: PartialFunction[Int, String] = { case 2 => "two" }val three: PartialFunction[Int, String] = { case 3 => "three" }val partial = two orElse threepartial(2) //==> two
- 类
class Calculator {val brand: String = "HP" //不写访问修饰符 默认为public//会为public的 val 字段生成getter 函数 def brand:String = this.brand//java中允许字段和方法同名 scala不允许 def 方法可被 val字段重写//会为public的 var 字段生成 getter & setter函数 brand & brand:=def add(m: Int, n: Int): Int = m + n}val calc = new Calculatorcalc.brand //==> HP/** 1) 构造函数*/class Calculator(brand: String) {require(brand != "") //参数验证/*** A constructor.*/val color:String = brand match {case "TI" => "blue"case "HP" => "black"case _ => "white"}//副构造器def this() = this("HP")// An instance method.def add(m: Int, n: Int): Int = m + n}/** 2) 参数化字段*/class Calculator(val color: String) {..}//等效class Calculator(brand: String) {val color: String = brand //去除一次累赘赋值//color的gtter..}//也可重写字段class SubCalculator (override val color: String) extends Calculator(color) {..}val sc = new SubCalculator("yellow");sc.color //==> "yellow"/** 3) 函数和方法的区别*/class C {var acc = 0def minc = { acc += 1 }val finc = { () => acc += 1 }}val c = new Cc.minc //c.acc = 1c.finc //==> () => Unit 返回一个函数c.finc() //c.acc = 2 执行返回的函数val f = c.fincf() //c.acc = 3 字段acc被闭包给带出了实例外 可怕的能力/** 4) 抽象类*/abstract class Shape {def getArea():Int // 方法声明 交给子类实现val width:Int // 字段声明 交给子类实现 与Java不一样val height:Int // 如果想一样就这样声明 val width:Int = _} //这么看起来Shape目前更像java接口//***不可实例化 但可以 实例化匿名子类val s = new Shape {def getArea():Int = width * heightval width:Int = 1val height:Int = 2}s.getArea() //==> 2
- Traits 特质
特质是一些字段和行为的集合,可以扩展或混入(mixin)你的类中
与Ruby模块类似 但是trait更有特色
一个类可以混入多个特质 特质可以叠加
trait Car {val brand: String}class BMW extends Car {val brand = "BMW"}//或 对象也可混入特质 **让对象瞬间拥有某能力 比猴子补丁安全些class BMWval b:Car = new BMW with Car {val brand = "BMW"}//或 实例化 traitval c:Car = new Car {val brand = "抵制日货"}//何时用特质和抽象类?//a. 优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类//b. 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。//特质可以叠加//特质里面的super调用是动态绑定的abstract class Base {def call(x:Int):Int}class SubB extends Base { //普通类只对参数安全加1def call(x:Int) = this.synchronized {x + 1}}trait Doubling extends Base { //*只能被Base子类混入abstract override def call(x:Int) = { //必须这样声明方法super.call(2 * x)}}trait Filtering extends Base { //*只能被Base子类混入abstract override def call(x:Int) = {if(x >= 0) super.call(x) else x}}new SubB with Doubling with Filtering call -1//==> -1 先执行 Filtering的方法 直接返回new SubB with Doubling with Filtering call 1//==> 3 一次 filtering、doubling、和subB的方法new SubB with Filtering with Doubling call -1//==> -2 先doubling 后被 filtering 过滤
- object 单例对象
单例对象用于持有一个类的唯一实例。通常用于工厂模式。
原静态方法和字段都定义在单例对象中。
单例对象可以和类具有相同的名称,此时该对象也被称为“伴生对象”。我们通常将伴生对象作为工厂使用。
class Bar(foo: String)object Bar {def apply(foo: String) = new Bar(foo)}var b = Bar() //apply 方法会被调用
- 函数即对象
函数是一些特质的集合。具体来说,具有一个参数的函数是Function1特质的一个实例。这个特征定义了apply()语法糖,让你调用一个对象时就像你在调用一个函数。
class AddOne extends Function1[Int, Int] {def apply(m: Int): Int = m + 1}val plusOne = new AddOne()plusOne(1) //==> 2 apply被调用//或者object addOne extends Function1[Int, Int] {def apply(m: Int): Int = m + 1}addOne(1) //==> 2
这个Function特质集合下标从0开始一直到22。为什么是22?这是一个主观的魔幻数字(magic number)。我从来没有使用过多于22个参数的函数,所以这个数字似乎是合理的。
apply语法糖有助于统一对象和函数式编程的二重性。你可以传递类,并把它们当做函数使用,而函数本质上是类的实例。
这是否意味着,当你在类中定义一个方法时,得到的实际上是一个Function*的实例?不是的,在类中定义的方法是方法而不是函数。在repl中独立定义的方法是Function*的实例
//可以使用更直观快捷的extends (Int => Int)代替extends Function1[Int, Int]class AddOne extends (Int => Int) {def apply(m: Int): Int = m + 1}
- 样本类 Case Classes
使用样本类可以方便得存储和匹配类的内容。你不用new关键字就可以创建它们。
class Calculator(brand: String, model: String)//普通类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前缀
case class Calculator(brand: String, model: String)val hp20B = Calculator("hp", "20b")hp20B.brand //==> hphp20B.brand = "qq" //==> error val....
3.编译器自动添加了toString、hashCode、equals
用途
样本类主要用于模式匹配
val hp20b = Calculator("hp", "20B")val hp30b = Calculator("hp", "30B")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)//以上为在匹配变量上套用匹配calcType(Calculator("aa", "2"))//==> Calculator: Calculator(aa, 2) of unknown type
- Map 与 Tuple(元组)
Map(1 -> 2)Map("foo" -> "bar")// 1->2 为 (1).->(2) 返回 元组(1,2)Map(1 -> 2) == Map((1,2)) //==> true//映射的值可以是映射甚或是函数。Map(1 -> Map("foo" -> "bar"))def addOne(x:Int) = x + 1val map = Map("addOne" -> { addOne(_) }) //偏应用函数map("addOne")(1) //==> 2//或 函数字面量val map = Map("addOne" -> { x:Int => x + 1 })
- Map 与 Option
map get方法 返回Option[T]类型
val map = Map(1 -> 1, 2 -> 2, 3 -> 3)map(1) //==> 1map(4) //==> java.util.NoSuchElementException: key not found: 4m contains 4 //==> false 这种方式来安全获取//或用get方法m get 1 //==> Some(1)m get 4 //==> None//针对 Option 处理方式m get 4 getOrElse 0 //==> 0m get 4 match {case Some(n) => ncase None => 0}//==> 0