编程语言设计中提到,一门语言包含了语法,语义,语用几个部分;语法是对语言规则的描述,语义是对语法单位,单词符号意义的定义;而了解语言的语法规则,语言特性,社区支持,生态发展是入门一门语言的基本过程。
接下来我先从常见一些基本语法来一起探索Scala:
1.变量:
1)常量:又称不变量,表示变量定义后值不能再更改,scala语言中推崇使用不变量,用val关键字表示
val aa = 1 或 val aa: Int = 1
2) 变量:用var关键字表示
var bb = 1 或 var bb: Int = 1
bb = 2
3) lazy 值:
val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString // 定义的时候取值
lazy val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString //在words首次使用的时候取值,以后一直是这个值
def words = scala.io.Source.fromFile("/usr/share/dict/words").mkString //在每一次words使用的时候取值
初始化特征:懒值初始化的时候并没有取值,只有当你首次去调用他的时候他才执行;一般用于如下场景:
- 解决开销较大的语句
- 应对其他初始化问题如:循环依赖
- 开发懒数据结构的基础如:流(stream)
这点感兴趣的同学可以看看haskell的惰性求值
2.操作符:
操作符包括基本的运算操作符(+,-,/,*)和位操作符(&,|,^,>>,<<)操作方式与Java中类似,但是在Scala中原则中“一切都是对象”例:
val c = a - 1 等同于 val c = a.-(1) 假设a为一个Int对象,-实际是Int对象的方法
3.特殊方法:
1)apply方法
BigInt(123456)实际上等同于 BigInt.apply(123456)
apply在scala中是构建对象常用的方法如: Array(1,2,3,4,5)
2) unapply方法
case class ScalaUnapply(a: Int, b: String)
ScalaUnapply(1, "3") match {
case ScalaUnapply(a, b) => println(a)
case _ => println("match failure!!!")
}
ScalaUnapply类是个case类,默认提供unapply方法,所以实际执行match的流程是:
- ScalaUnapply类先调用自己的unapply方法并返回Option[(Int, String)];
- case 匹配后发现是个Option类会将其解开,并判断个数,然后将1赋值个a变量,"3"赋值给b变量;
类似的方法还有unapplySeq
4.控制结构
1)表达式与语句的区别:
- 表达式的目的是计算出一个值,而语句的目的是产生一个side effect。
- 表达式有值,而语句没有值只执行操作
- 表达式是可以被求值的代码,而语句是一段可执行代码;因为表达式可被求值,所以它可写在赋值语句等号的右侧,而语句不一定有值,所以像import、for和break等语句就不能被用于赋值。
在Scala中,几乎所有构造出来的语法结构都有值,也就是表达式。这可以使程序更加精简易读。
例:if表达式是有值的;语句块也是有值的,是最后一个表达式的值而对应Java中的void,Scala中有一个类型Unit,用来表示“无有用值”(void表示没有值,这里还是有差别的)
2)条件表达式:
if/else表达式在Scala中是有值的,除此之外与Java等语言没有什么差别。而有值所带来的效果,可以是下面这样子的:
val s = if (x > 0) 1 else -1
// 与上等效的另一种写法
if (x > 0) s = 1 else s = -1
如果在if和else两边的类型是不同的,比如:
if (x > 0) "positive" else -1
这时返回的类型将会是两个分支类型的公共超类型(这里是String和Int的公共超类型Any)
如果没有else部分,那么if表达式有可能不返回值,这时候就需要前面提到的Unit类型,写做()。
if (x > 0) 1
// 这相当于else部分返回Unit类型,相当于下面的写法
if (x > 0) 1 else ()
2)循环控制
for结构
Scala中有与Java类似的while循环和do循环,但却没有最常使用的 for ( ; ; )这种形式的循环(但还是有for循环的)。同时,Scala中也没有break和continue语句来控制循环。替代的解决方案就是使用变量和条件语句来进行循环的控制。要使用break时,可以有集中选项:
- 使用Boolean型的控制变量;
- 使用嵌套函数,可从函数中return;
- 使用Breaks对象中的break方法,这种方法中,控制权转移是通过异常来完成的,所以在追求时间上的效率时,要尽量避免使用。
import scala.util.control.Breaks._
breakable{
for (i <- 1 to 9){
if (i == 5) {
break
}
}
}
while结构
var i = 10
do {
i = i - 1
} while (i>0)
while (true){
}
高级for和for的推导式
在一个for循环中使用多个“generator”(生成器):
for (i <- 1 to 3; j <- 1 to 3) print ((10 * i +j) + " "
// 结果:11 12 13 21 22 23 31 32 33
在for循环中使用“filter ”(过滤器,守卫)来过滤一部分不符合条件的枚举情况:
for (i <- 1 to 3; j <- 1 to 3 if i != j) print ((10 * i + j) + " ")
// 结果:12 13 21 23 31 32
可以使用多个filter,中间需要使用分号分隔。可以在for表达式中使用任意多的定义,引入在循环中使用变量:
for (i <- 1 to 3; from = 4 - i; j <- from to 3) print ((10 * i + j) + " ")
// 结果:13 22 23 31 32 33
如果for表达式的循环体是以yield关键词开始的,那么该循环会构造出一个集合,这被称为for推导式。推导式生成的集合与第一个generator是类型兼容的。
for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar
// 生成"HIeflmlmop"
for yield结构的应用远不止于此,后面会单独谈到他
3)块表达式:
Scala中,{ }包含一系列的表达式,而且{ }最后返回的也是一个表达式——最后一个表达式。而赋值语句的值,是Unit类型的。这也大体上决定了在Java和C++中的 x = y =1 会出现的赋值语句,几乎不可能出现在Scala中。
4)语句终止:
分号不是必须的,但是使用它们不会带来坏处。能根据上下文判断出语句结尾的地方,都不需要使用分号。怎么判断是否语句结尾,以后在编码过程中会慢慢积累起感觉的。如果需要在单行中写多个语句,那么就需要使用分号了。而在写一个较长的语句时,如果需要分行写,要确保前面的行尾需要以不能用来语句结尾的符号来结束。比如说像是下面的操作符:
a = s0 + (v - v0) * t + // +说明了这里不是语句结束哦
0.5 + (a - a0) * t * t
// 下面这种写法就会出问题了
a = s0 + (v - v0) * t
+ 0.5 + (a - a0) * t * t
这里还可以发现的是,Scala推荐使用两个空格来缩进。由于判断语句终止的需要,Scala程序员更倾向于使用Kernighan & Ritchie风格的花括号(也就是Java中推荐的方法):
if (n > 0) {
r = r * n
n -= 1
}
5.函数与过程:
函数:
在Java中并没有函数这个概念,函数的功能是用静态方法来实现的。
def 函数名(指明类型的参数): 可选的返回值类型 = 函数体
def fac(n: Int) = {
var r = 1
for (i <- 1 to n) r = r * i
r
}
只要函数不是递归的,就不需要给出函数的返回值类型(因为有类型推断)。一个递归的函数需要如下写:
def fac(n: Int): Int = if (n <= 0) 1 else n * fac(n - 1)
在Scala中,并不时常使用return来返回值。
过程:
过程是不返回值的函数。过程的定义与函数很类似。函数体在花括号中,而且花括号前没有=号,返回值是Unit类型的,这就是过程了。(也可以写出=号,并且写明返回值是Unit类型来定义过程。)通常,过程没有返回值,只是为了副作用而调用一个过程.
6.异常处理
scala中没有“受检异常”的概念,scala的设计者认为“受检异常”并不是最好的如:
public static void main(String[] args) throws IOException {
// 指在编译器对异常进行检测,如果你的方法要抛出异常,就必须声明异常
}
过早的捕获异常或者采用通用的异常并不是最好的;
throw 表达式有特殊的类型Nothing来标识
val res: Int = {
if (x == 0) {
1
}
else throw Exception
}
异常捕获结构
try{
}
catch{
case e: Exception =>
case _ =>
}
finally{
}