wangzha欢迎你们
本章内容:
1、前言
2、if表达式
3、while循环
4、for循环
5、尾递归
6、try表达式处理异常
7、match、break、continue
8、控制抽象
前言
Scala的内建结构和Java相差无几,她从语法层面支持函数式字面量。因为代之以基本语法之上逐一添加高级的内建结构,Scala可以把它们实现到函数库中(即控制抽象
)。几乎所有的Scala控制结构都会产生某个值
,这是函数式语言所采用的方式,程序被看成是计算值的活动,因此各个部件也不例外。Scala也具备三元操作符模型,但仍称为If,即Scala的If会返回值,包括for、try和match都会产生值。这样程序员可以省略代码
,否则必须要新建一个临时变量来存放控制结构运算出来的结果。
if表达式
指令式if表达式:
var fileName = "default.txt"
if(!args.isEmpty){
fileName = args(0)
}
函数式if表达式:
val fileName = if (!args.isEmpty) args(0) else "default.txt"
函数式if表达式的优势在于:val
体现了函数式的编程风格,并且具有与Java的final变量类似的效果。它可以告诉读者,代码变量的值将不会再改变,从而节省了他们审查变量作用域的所有代码,以检查它是否改变。另外,val
相对于var
来说,更好地支持等效论。在表达式没有副作用的前提下,引入的变量等效于它的表达式。
无论何时,println(fileName)
等价于println(if (!args.isEmpty) args(0) else "default.txt")
。val有助于安全地执行这类重构以不断革新代码。
while循环
指令式while计算最大公约数:
def gcdLoop(x:Long,y:Long):Long = {
var a = x
var b = y
while(a!=0){
a = b % a
b = temp
}
b
}
函数式计算最大公约数:
def gcd(x:Long,y:Long): Long = if(y == 0) x else gcd(y , x%y)
Scala的while循环与其他语言一致,为do-while循环
,do-while被称为"循环"而不是表达式
的原因,是因为它们不能产生有意义的结果,即结果为Unit,是表示存在并且唯一存在类型为Unit值,称为unit value,写成()。()的存在是Scala的Unit不同于Java的void的地方。方法体没有"="
的时候,返回值即为Unit,比如def greet() {println("xxx")}
由于while循环不产生值,因此它经常被纯函数式语言所抛弃。但是Scala仍然保留了while,因为有时候指令式的解决方案更加易懂。通常不介意使用while循环就如同不介意使用var一样
。
for循环
1、for循环的基本方式
val filesHere = (new java.io.File(".")).listFiles
for(file<-filesHere) println(file)
上述代码通过发生器(generator)的语法file<-filesHere遍历filesHere的元素。每一次枚举,名为file的新的val就被元素值初始化。
//索引1到4的元素
for(i<-1 to 4) println("Iteration"+i)
//索引1到3的元素
for(i<-1 util 4) println("Iteration"+i)
注意Scala的枚举规避了索引是从0还是从1开始,最后索引是加1还是要减1的操作,这种问题很容易出错。
2、for循环的过滤
单个过滤条件:
val filesHere = (new java.io.File(".")).listFiles
for(file<-filesHere if file.getName.enWith(".scala")) println(file)
多个过滤条件:
val filesHere = (new java.io.File(".")).listFiles
for(file<-filesHere
if file.isFile;
if file.getName.endsWith(".scala")) println(file)
3、for循环的嵌套
def fileLines(file:java.io.File) = scala.io.Source.fromFile(file).getLines.toList
def grep(pattern:String)=
for(
file<-files
if file.getName.endsWith(".scala")
line<-fileLines(file)
if line.trim.matches(pattern)
) println(file+":"+line.trim)
grep(".*gcd.*")
4、滤间变量绑定
请注意前面的代码段中重复出现的表达式line.trim。这是一个不可忽略的计算,我们只希望它计算一次。这可以通过用”=”把结果绑定到新变量实现。绑定的变量被当作val引入和使用,不过不带关键字val。
def fileLines(file:java.io.File) = scala.io.Source.fromFile(file).getLines.toList
def grep(pattern:String)=
for(
file<-files
if file.getName.endsWith(".scala")
line<-fileLines(file)
trimmed = line.trim
if trimmed.matches(pattern)
) println(file+":"+line.trim)
grep(".*gcd.*")
5、制造新的集合
for循环表达式在每次执行的时候都会产生一个新值,本例中file。当for表达式完成时,结果将是包含了所产生所有值的集合对象。集合对象类型基于枚举子句处理的集合类型,即循环了什么集合类型就返回什么集合类型
。
def scalaFiles =
for(
file<-files
if file.getName.endsWith(".scala")
) yield file
下面这段代码返回了.scala
文件中当前行含有for字符串
的那些行的长度集合。
val =
for(
file<-files
if file.getName.endsWith(".scala")
line<-fileLines(file)
trimmed = line.trim
if trimmed.matches(".*for.*")
) yeild trimmed.length
尾递归
1、尾递归概念
请回想一下上面所说到的while循环实现最大公约数的两种方式,指令式和函数式。
这2个版本就就简洁性和避免var来说,函数式胜出;但指令式效率是否会更加出色呢?因为函数式的地方毕竟是重新调用方法,而指令式只是调到代码的头部。实际上Scala有一个重要的优化称为尾递归,这个优化导致函数式和指令式的效率相差无几。尾递归就是在函数的最后一个动作是递归。Scala编译器检测到尾递归就用新值更新参数,然后把它替换成一个
。
回到函数开头的跳转
2、尾递归函数追踪
尾递归函数将不会为每个调用制造新的堆栈结构,所有的调用将在一个结构内执行,这可能会让检查程序的堆栈跟踪信息并失败的程序员感到惊奇。我们看一下下面2个运行结果图,尾递归和非尾递归控制台的显示。我们可以通过-g :notailcalls
来关闭该选项,定义了该选项,我们就可以跟踪长长的堆栈了。
3、尾递归的局限性
Scala里的尾递归的局限性很大,因为JVM指令集实现更加先进的尾递归形式变得很困难。Scala仅优化了直接递归调用使其返回同一个函数。
- 如果递归是间接的,就像下面的例子里
两个相互递归的函数
,就没有优化的可能性了:
def isEven(x:Int):Boolean = if(x==0) true else isOdd(x-1)
def isOdd(x:Int): Boolean = if(x==0) false else isEven(x-1)
- 如果最后一个调用是一个函数值,也不能获得尾递归,比如下面的代码:
funValue
变量指向一个实质是包装了nestedFun
的调用的函数值。当你把这个函数值应用到参数上,它会转向把nestedFun
应用到同一个参数,并返回结果。总之,尾递归的调用限定了方法或嵌套函数必须在最后一个操作调用本身,而不是转到某个函数值或者什
么中间函数的情况。
val funValue = nestedFun_
def nestedFun(x:Int){
if(x != 0) {
println(x);funValue(x-1)
}
}
尾递归优化相关文章:
http://blog.csdn.net/JasonDing1354/article/details/50525735
try表达式处理异常
除了下面几点以外,Scala中的try
表达式和Java中的一模一样。
- 代码形式略有不同,看下面的Scala的
try
表达式
try{
xxx
}catch{
case e : FileNotFoundException => println("xxx")
case e : IOException => println("xxxx")
}finally{
do()
}
- Scala中的
try、catch、finally
都会产生值。
def url(path:String) = {
try{
new URL(path)
}catch{
case e : MalformedURLException => new URL("www.wangzha.com")
}
}
- Scala中如果
finally
块没有显式的调用return
,则返回的值会被废弃。
def f():Int = try{ return 1 } finally { return 2 }
调用f()后返回2
def g():Int = try{ 1 } finally { 2 }
调用g()后返回1
match、break、continue
1、match
Scala中的match
表达式类似于Java中的switch
语句,主要区别如下:
match
可以接收任何类型的东西作为样本进行比较,而Java中只支持整型和枚举。match
中的break
,是隐含的,也就是说,不论如何,不允许从上一个备选项到下一个备选项中。match
也可以产生值。
val firstArg = if(args.length > 0) args(0) else ""
val result = firstArg match{
case "aaa" => println("aaa")
case "bbb" => println("bbb")
case "ccc" => println("ccc")
}
2、break、continue
从此不在有break、continue
。Scala中可以用if替换每一个continue
,用布尔变量替换每一个break
,或者其他方式。接下来,我们看一段代码的三种形态
- 第一种(Java中)
int i = 0;
boolean foundIt = false;
while(i<args.length){
if(args[i].startsWith("_")){
i++;
continue;
}
if(args[i].endsWith(".scala")){
foundIt = true;
break;
}
i++;
}
第二种代码:用if替换每一个continue
,用布尔变量替换每一个break
var i = 0
var foundIt = false
while(i<args.length&&!foundIt){
if(!arg[i].startsWith("_")){
if(args[i].endsWith(".scala")){
foundIt = true
}
}
i++
}
第三种代码:递归调用
def searchForm(i:Int):Int = {
if(i>=args.length) -1
else if(args[i].startsWith("_")) searchForm(i+1)
else if(args[i].endsWith(".scala")) i
else searchForm(i+1)
}
控制抽象
该内容留到下一章,结合函数一起学习。