@MiloXia
2014-12-08T18:56:31.000000Z
字数 8649
阅读 3156
- 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 //不要是null
while (it.hasNext && result == None) {
val Pair(x1, y) = it.next
if (x == x1) result = Some(y)
}
result
}
Option(null) //==> None //对空值包装比较好Option(getReward())为空时就为None
Some(null) //==> Some(null)
Some(1,2,3) == Option(1,2,3) == Some((1,2,3)) //内部是元组
None getOrElse 0 //==> 0
Some(1) getOrElse 0 //==> 1
/*
* Option 实现了 map, flatMap, and filter 接口
* (这样 Scala编译器就允许在 'for'循环里使用它了...)
*/
(for (val user <- User.find(id)) //可能返回None
yield {
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 :: _ => true
case _ => 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)
/等效for
for(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}
//==> true
val f = (x: Int) => x + 1 //函数字面量
//在将一个匿名函数作为参数进行传递时,这个语法会经常被用到。
List(1,2,3) map {a => a + 1}
/*
* 2) 部分应用函数 和柯理化差不多
*/
val f = (x:Int, y:Int) => x + y
val fp = f _
fp()(1,2) //==> 3
val fq = f(1, _:Int)
fp(2) == fp.apply(2) //=> 3
/*
* 3) 柯里化函数
*/
def multiply(m: Int)(n: Int): Int = m * n
multiply(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+y
val a = (add _).curried
a(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 three
partial(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 Calculator
calc.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 = 0
def minc = { acc += 1 }
val finc = { () => acc += 1 }
}
val c = new C
c.minc //c.acc = 1
c.finc //==> () => Unit 返回一个函数
c.finc() //c.acc = 2 执行返回的函数
val f = c.finc
f() //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 * height
val width:Int = 1
val height:Int = 2
}
s.getArea() //==> 2
- Traits 特质
特质是一些字段和行为的集合,可以扩展或混入(mixin)你的类中
与Ruby模块类似 但是trait更有特色
一个类可以混入多个特质 特质可以叠加
trait Car {
val brand: String
}
class BMW extends Car {
val brand = "BMW"
}
//或 对象也可混入特质 **让对象瞬间拥有某能力 比猴子补丁安全些
class BMW
val b:Car = new BMW with Car {
val brand = "BMW"
}
//或 实例化 trait
val c:Car = new Car {
val brand = "抵制日货"
}
//何时用特质和抽象类?
//a. 优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类
//b. 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。
//特质可以叠加
//特质里面的super调用是动态绑定的
abstract class Base {
def call(x:Int):Int
}
class SubB extends Base { //普通类只对参数安全加1
def 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 //==> hp
hp20B.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 + 1
val 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) //==> 1
map(4) //==> java.util.NoSuchElementException: key not found: 4
m contains 4 //==> false 这种方式来安全获取
//或用get方法
m get 1 //==> Some(1)
m get 4 //==> None
//针对 Option 处理方式
m get 4 getOrElse 0 //==> 0
m get 4 match {
case Some(n) => n
case None => 0
}
//==> 0