首页 > 代码库 > Programming In Scala Reading Note 6
Programming In Scala Reading Note 6
Built-in Control Structures 内置控制结构
1 if 控制结构
scala的if跟java的if结构上相同,功能上也差不太多,scala的说法是,if能够返回一个值
val fileName = if (!args.isEmpty) args(0) else "default.txt"
这个语句相对普通,但是这里没有引入一个var,而是使用的val。
println(fileName) 等价于 println(if (!args.isEmpty) args(0) else "default.txt"), 这也是val的一个优势,叫做equational reasoning
2 loop 循环结构
do{...} while(condition)
while(condition) {...}
跟java基本一样,但是这里有一点需要说明的是:赋值语句返回值是Unit,而不是被赋的值,如:
var newLine:String = ""
while ((newLine = readLine()) != "") {...}
如果在java中,readLine()返回值为""的话,那么这个condtion就是不正确的,就要跳出了。
而在scala中,赋值语句的返回值是Unit,它永远不等于"",所以就永远不会terminate了。
因为loop并不返回一个值,所以他不怎么出现在functional language中,作为替代,会使用递归的方式:
def greatestCommonDivisor(a:Int, b:Int):Int =
if (b==0) a else greatestCommonDivisor(b, b%a)
3 for 结构
遍历集合
for(file <- files){...}
file <- files 称为generator,用来定义一个确定名称的为file的var变量,并且给它赋一系列的值,用以for循环
<- 右侧的表达式为generator expression,用于生成一系列的值
generator expression可以是任何形式的collection
1 to 5 :Range(1, 2, 3, 4, 5)
1 until 5 : Range(1, 2, 3, 4)
过滤集合
只对满足某些条件的集合元素作处理的时候,会用到如:
for(file <- files if file.getName.endsWith(".scala")){...}
多个条件:
for(
file <- files
if file.isFile // 没有(),没有;或,
if file.getName.endsWith(".scala")
){...}
中间不包含任何的逗号,分号等
I believe this is handsome。。。
嵌套循环
for(
file <- files
if file.isFile // 没有(),没有;或,
if file.getName.endsWith(".scala"); //注意这个分号!!!
line <- scala.io.Source.fromFile(file).getLines.toList
if line.trim.match(".*gcd.*")
){...}
for body里面可以包含当前的file信息和当前line信息。。。
如果想要去掉这个;的话,我们可以把for后面的小括号换成大括号
for{
file <- files
if file.isFile // 没有(),没有;或,
if file.getName.endsWith(".scala") // 这里不再有分号,因为我们用的是大括号{}
line <- scala.io.Source.fromFile(file).getLines.toList
if line.trim.match(".*gcd.*")
}{...}
循环中绑定变量
for(
file <- files
if file.isFile // 没有(),没有;或,
if file.getName.endsWith(".scala");
line <- scala.io.Source.fromFile(file).getLines.toList
trimmed = line.trim
if trimmed.match(".*gcd.*")
){...}
引入的这个trimmed是一个val,后续的代码中可以使用它。
for循环生成一个新的集合变量
for clauses yield body
首先这个yield的位置需要注意,在for条件的后面,for body的前面,
for{condition...} yield { for body}
这个for语句是能够返回一个对象的集合的
4 try
Exception,发生一个Exception的时候,会被无情地抛出,然后这个异常会按照方法调用栈的调用顺序往回返,直到有一个方法处理了这个Exception,不然就一直回溯到最后一个方法,然后抛给编译器。
throw表达式
throw new IllegalArgumentException,这样就产生了一个错误参数的异常,也就是说throw语句是有返回值的。
val half = if (n % 2 == 0) n / 2 else throw new RuntimeException("n must be even")
上面的代码中,如果传递进来的n不是偶数的话,就会抛出一个运行时异常,这个异常会被处理,而造成half没有值,没有被初始化。所以任何想要使用这个half的程序都不会有什么问题。
catch表达式
catch语句被用来跟scala中的一个很重要的概念:模式匹配(pattern matching)来保持一致。
try { val f = new FileReader("input.txt") // Use and close file} catch { case ex: FileNotFoundException => // Handle missing file case ex: IOException => // Handle other I/O error}
catch中的每一个case代表一种情况,编译器会检查其类型是否为指定的类型,如果是的话,执行后面的语句,如果不是继续直到找到对应的异常类型,如果都不满足的情况下,会终止这个catch语句,抛异常给方法的调用者。
finally表达式
基本上是为副作用而设置的一个表达式。
The result is that of the try clause if no exception is thrown, or the relevant catch clause if an exception is thrown and caught.
If an exception is thrown but not caught, the expression has no result at all.
The value computed in the finally clause, if there is one, is dropped.
try-catch-finally表达式的值:
如果没有异常抛出,那么其值就是try表达式的值。
如果有异常抛出,且被catch捕获了,那么就是这个捕获异常语句的值。
如果异常抛出,且没有被捕获住,那么这个表达式就没有值。而不是finally的值。
尽管finally语句中包含表达式返回值,其值也会被丢弃掉。
不得不说这很残酷,很难理解。scala提供的解释是,finally语句的用途就是为了:
1 关闭一个文件
2 关闭一个套接字
3 关闭数据库连接等等
def some = try {return 1} finally {return 2}的返回值确实是2,但是这并不是scala推荐的做法。
def some = try { 1 } finally { 2 }的返回值是2,这才是scala推荐的形式。。。
The best way to think of finally clauses is as a way to ensure some side effect happens, such as closing an open file.
finally就是为了副作用而生的。
5 match表达式
_, 通配符wildcard,用来充当一个不能确定值的选项。
match的适用对象要比java的switch要多,java仅仅适用于Integer类型的或者Enum类型的,scala的match能够适应任意常量。
sth4match match {
case some1 => //do something for some1
case some2 => //do something for some2
case _ => // do something for other condition
}
另外match语句的case中没有break,这个break是默认追加进来的,在每一个alternative后面。
最后一个最重要的区别应该是每一个case都会有一个返回值。最好不要仅仅让每一个case产生副作用。
6 不用beak和continue
替换break和continue的方式就是尝试用递归来实现需求,假如遍历数组元素,找到第一个不以-开头的,且以.scala结尾的元素
def searchFrom(index:Int, args:Array[String]):Int =
if (index >= args.length) return -1 // 提供的index有问题
else if (args(index).startsWith("-")) searchFrom(index+1, args) // 发现有毛病,继续下一个,也就是实现了continue
// 注意他出现的次序,应该把该排除掉的情况放到正确的情况前面,以及时过滤掉
else if (args(index).endsWith(".scala")) index // 值发现了,返回给这个方法的调用者,break的功能实现了
else searchFrom(index+1, args) // 如果尚未发现,就继续下一个,这个语句一定要放到最后执行,也就是while循环还要继续
// 递归方法必须指定返回值,不然方法体中调用方法产生的返回值编译器根本就无从得知。
7 variable scope 变量的作用域
跟java基本相同,不过java中如果声明了一个变量a,那么这个a就不能够再被随便声明了。
而Scala中,在一个括号里面就可以定义任意一个名称的变量了,不管是否存在,但是需要在大括号的外面追加一个分号。。。不知道什么意思。。。
8 重构指令式编码
指令式编码的一个显著特点就是var的引入和副作用的产生。
将var去除掉,只引入必要的val。
不要副作用,而只是把需要的信息提供给方法的调用者。
函数式编程的一个显著特点就是,方法特别多,但是方法功能又非常具体。
举一个书上的例子说明:
打印一个1到10的乘法表格,java的方式就是双重for循环。
scala也同样是双重for循环,只不过分配到了两个方法中,详细说一下:
1 1 to 10 行
2 1 to 10 列
3 保证格式不错乱
def makeMultiTable() = {
// 一共十行数据,把每行数据的获取交给makeRow方法
val tableSeq = for (row <- 1 to 10) yield makeRow(row)
// 把获得的seq整理一下
tableSeq.mkString("\n")
}
// 每一行数据是由每一列数据组成的,交给makeRowSeq去处理
def makeRow(rowIndex:Int) = makeRowSeq(rowIndex).mkString
// 对每一个点进行处理,获得对应的行的数据,Vector
def makeRowSeq(rowIndex:Int) =
for(col <- 1 to 10)
yield
{
val point = (row * col).toString
val gap = " " * (4 - point.length)
// RETURN ELEMENT
gap + point
}