scala 笔记
1. scala 的函数的参数默认都是val类型的
2. _ 参数占位符
3. 偏应用函数,类似于python 函数赋值到另外一个变量:
def sum(a,b,c): return a+b+c sum(1,2,3) // 输出6 a = sum a(1,2,3) // 输出6
在scala中:
def sum(a:Int, b:Int, c:Int) = a + b + c sum(1,2,3) // 输出6 val a = sum _ a(1,2,3) //输出6
val c = a
c(1,2,3) //输出6
还可以这样用 val b = sum(1, _: Int, 3); b(2) // 输出6
4. 闭包
短文本函数(x:Int)=> x>0, 现在考虑这种情况的短文本函数: (x:Int)=> x>more
more是什么呢? more应该是一个变量,但是没有在该函数文本中定义。x 是一个绑定变量,因为其在函数文本中有定义
与python中类似,闭包返回的是一个函数值,这个函数值的文本中访问了该闭包的局部变量,如
def makeIncreaser(more: Int) = (x: Int) => x + more
闭包makeIncreaser 返回函数值 (x: Int) => x + more, 该函数值访问了闭包的变量more
scala> val inc1 = makeIncreaser(1)
inc1: (Int) => Int = <function>
scala> val inc9999 = makeIncreaser(9999)
inc9999: (Int) => Int = <function>
创建了两个函数。 尽管more 是一个已经返回的函数调用的参数,scala编译器会在堆中重新安排1 和 9999的存在以让返回的函数值去捕获。 除了参数还可以是任意val,var。
5. 变长参数
def func(fmt:String, args:Type*) ....
用 args:Type*表示变长部分,在函数内部,args 会被当做 Array[Type]
调用时候 : func("format", a , b)
或者 : func("format", array: _*) 用 _* 表示把array展开。
类似于python中: def func(fmt, *args): pass
func(fmt, *argList) // py中吧*args 当做tuple
6. 尾递归
在递归函数最后一个动作调用自己的函数,被称为尾递归:tail recursive。Scala编译器检测到尾递归就用新值更新函数参数,然后把它替换成一个回到函数开头的跳转。 注意最后一个动作是调用自身,甚至连自身的偏应用函数都不行。
7. curry 化
感觉curry化与偏应用函数指定部分参数差不多
def curriedSum(x: Int)(y: Int) = x + y
curriedSum(1)(2)
这里发生的事情是当你调用curriedSum,你实际上背靠背地调用了两个传统函数。第一个函数调用带单个的名为x的Int参数,并返回第二个函数的函数值。第二个函数带Int参数y.
val onePlus = curriedSum(1)_
onePlus(2) // 3
8. 如果函数只有一个参数,那么调用时候可以用{}代替(), scala里这个机制主要是考虑到函数作为参数的时候,如果直接写函数文本采用{}可以让程序员写包围在{}的函数文本。
println("hello world") 可以写成println{"hello world"}
对于两个参数或者更多参数的函数不能用{}代替(),但可以结合偏应用函数来使用或者把两参函数定义成curry函数:
def func(a:Int, b:Int) = a + b val funp = func(_:Int, 3) println{funp{2}} def funcurry(a:Int)(b:Int) = a + b println{funcurry(3){5}}
不过上面例子中的参数不是函数,在非函数情况下没必要用{}
9. 叫名参数
def myAssert(predicate: ()=>Boolean) {
if(!predicate){
throw new AssertionError
}
}
调用: myAssert{() => 5 > 3}
叫名参数,要定义参数的类型开始于=>而不 是() =>, 叫名类型中,空的参数列表,(),被省略,它仅在参数中被允许
def myAssert(predicate: =>Boolean) { .... }
调用: myAssert(5>3)
10. Scala 里禁止在同一个类里定义同名的字段和方法
11. Null 是所有类的子类,Null是null类型的引用;Nothing是任何类型的子类
12. Scala 类型也是单一继承的
13. trait 类似于java中的interface,不过允许实现方法,也是类型也占内存的
我觉得引入特征,其实为了有很像c++中的多继承。 trait 与类没多大区别,除了不能有类参数;类中的super是静态绑定的,特征中的super是动态绑定的,需要看混入到具体哪个类才能确定super指谁;特征具有堆叠性质,这点多继承没有(多继承的supper调用也必须明确哪个上层类的方法,属于静态绑定)。 混入多个特征,越后的越先被应用
14. Ordered trait, 混入这个ordered 特征,你只需要实现compare 方法,就能自动的定义了 <, >, <=, >= 4个方法
15. 特征线性化顺序:
16. 断言
Scala 在方法里除了可用 assert() 方法像 Java 那样进行断言,还可以使用 ensuring() 方法在返回结果的分支的花括号同一行上进行断言。它们不同的是 assert 可以随意放在哪里对任何的 boolean 类型进行断言,而 ensuring 是用来对返回结果行断言的,所以它必须尾随返回结果处。 assert 和 ensuring 方法都是定方在 Predef 中的,所以可以直接写。 断言可以使用jvm里的-ea和-da命令行标志开放和禁止。
01
02
03
04
05
06
07
08
09
10
|
private def widen(w : Int) : Element = { if (w < width){ this } ensuring( _ .width > 10 ) //这里欲断言返回结果 this,所有两 if 后的花括号不能省略 else { val left = elem( ' ' , (w - width)/ 2 , height) var right = elem( ' ' , (w - width - left.width, height) left beside this beside right } ensuring(w < = _ .width) //断言的是上一行 left beside this beside right 结果 } ensuring((w + _ .width) > 100 ) //ensuring 断言可以放在方法体外了,用来断言最终的结果 |
17. case 类
a. 自动生成与类同名的工厂方法,于是A(x) 等同于 new A(a)
b. 自动为构造参数隐式的加上了val (scala 类的构造参数前加var/val ,自动生成相应的成员以及存取方法)
c. 自动实现toString, hashCode, equals方法
d. 支持模式匹配
18. 模式匹配
匹配:
- arg match {
- case "salt" => println("pepper")
- case "chips" => println("salsa")
- case "eggs" => println("bacon")
- case _ => println("huh?")
- } //match表达式有值
通配模式:_ 常量模式:5,“hello”, Nil 只匹配空列表 变量模式: E match { case pi => 3.14159265} ,变量模式可以匹配任何输入,因此之后的情况不会被访问到的,可以用常量代替(小写字母开头的pi (val pi)会被当做变量,但`pi`则被当做常量)
构造器模式 序列模式 元组模式(元组模式不同于序列模式的地方是,其不能指定_*,必须是固定元素个数匹配)
类型模式(类型擦除,数组除外)
19. 模式守卫: 就是在模式匹配时候 加 if 表达式,为真才匹配成功
20. 封闭类: sealed class, 除了在类定义所在的文件外,不能再其它地方添加任何新的子类。这个规则将有利于编译器为你的模式匹配检测出未匹配的模式(当然你也可以用(e: @unchecked) match {}方式禁掉编译器穷举所有模式检查)
21. Option类型,如Option[String]类型的变量是可选的String,可以用模式匹配分离其值Some或者None
def show(x: Option[String]) = x match {
case Some(s) => s
case None => "?"
}
22. 模式匹配的主要应用: 变量定义,偏函数样本,for表达式
23. Option,以下来自effective scala
Option
Option
是一个包含或者不包含某些事物的容器。
Option的基本接口类似于:
1
2
3
4
5
|
trait Option[T] { def isDefined: Boolean def get: T def getOrElse(t: T): T } |
Option本身是泛型的,它有两个子类:Some[T]
和None
我们来看一个Option的示例: Map.get
使用Option
来作为它的返回类型。Option的作用是告诉你这个方法可能不会返回你请求的值。
1
2
3
4
5
6
7
8
|
scala> val numbers = Map( 1 -> "one" , 2 -> "two" ) numbers: scala.collection.immutable.Map[Int,String] = Map(( 1 ,one), ( 2 ,two)) scala> numbers.get( 2 ) res0: Option[java.lang.String] = Some(two) scala> numbers.get( 3 ) res1: Option[java.lang.String] = None |
现在,我们要的数据存在于这个Option
里。那么我们该怎么处理它呢?
一个比较直观的方法就是根据isDefined
方法的返回结果作出不同的处理。
1
2
3
4
5
6
7
|
//如果这个值存在的话,那么我们把它乘以2,否则返回0。 val result = if (res1.isDefined) { res1.get * 2 } else { 0 } |
不过,我们更加建议你使用getOrElse
或者模式匹配来处理这个结构。
getOrElse让你可以很方便地定义一个默认值。
1
|
val result = res1.getOrElse( 0 ) * 2 |
模式匹配可以很好地和Option
进行配合使用。
val result = res1 match { case Some(n) => n * 2 case None => 0 }
25. covariant 要求类的成员是非可变的(不会改变成员内容),同时不能有这样的成员函数:以参数类型为参数类型
26. 协变与逆变http://www.tuicool.com/articles/vaAnmq