Scala学习之控制结构、尾递归及控制抽象

wangzha欢迎你们
本章内容:
1、前言
2if表达式
3while循环
4for循环
5、尾递归
6try表达式处理异常
7matchbreakcontinue
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)
}

控制抽象

该内容留到下一章,结合函数一起学习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值