码迷,mamicode.com
首页 > 其他好文 > 详细

[Scala基础系列 05]Scala控制结构

时间:2015-08-11 13:54:13      阅读:194      评论:0      收藏:0      [点我收藏+]

标签:

1.If语句

Scala的If语句可以完成其他语言中If语句,于此同时,if/else通常还有值,可以用来赋值,或者代替三元条件运算符(?:)。不过它可以比条件运算符更强大,因为你可以在if-else里面写很复杂的程序块。

1.1.普通的If语句

package com.tv189.foundation

/**
 * Created by molyeo on 2015/7/30.
 */
object IfCondition {
  def main(args: Array[String]) {
    val checkPoint = 80
    def check(current: Int): Unit = {
      if (current >= checkPoint) {
        println("Success")
      } else {
        println("Fail")
      }
    }
    check(80)
    check(90)
    check(30)
  }
}

1.2.用If语句赋值

if/else语句能用来赋值,进而代替?:运算符,得益于Scala的一个重要特点,在Scala中,每个语句块都有值,就是该语句块最后一个语句的值。请看下面的代码。

def printAbs(x: Int) {
  val abs = if(x < 0) -x else x
  println("The Abs of %d is %d".format(x, abs))
}

def abs(x: Int) = if(x < 0) -x else x

printAbs(-5)
println(abs(-6))

第一个函数printAbs里,用了一个if-else语句给abs赋值,这个语句的作用就相当于C语言或者Java中的?:操作符。 进一步的,由于语句块的最后一个语句的值,是整个块的值,语句块只有一个语句时,大括号{}可以省略。因此,求绝对值的函数可以简略至只有一行。

2.While语句

Scala的While语句与其他语言中While语句类似,有经验的程序员可以跳过这一小节。与If语句不同,While语句本身没有值。或者说,整个While语句的结果是Unit类型的()。

2.1.while循环

与大部分语言一样,Scala中的while循环包括循环体和循环条件两部分,循环条件满足时,循环体会一直执行下去。

package com.tv189.foundation

/**
 * Created by molyeo on 2015/7/30.
 */
object WhileCondition extends  App{


    def reverse(number: Int) = {
      var result = 0;
      var temp = number;
      while (temp != 0) {
        result = 10 * result + temp % 10;
        temp /= 10;
      }
      result;
    }

    println("The result of reverse(12345) is "+reverse(12345))

    def sum(input: Int) = {
      var total = 10;
      var temp = input;
      while (temp > 0) {
        total = temp + total;
        temp = temp - 1;
      }
      total;
    }
    println("The result of sum(10) is "+sum(10));

}

运行结果

The result of reverse(12345) is 54321
The result of sum(10) is 65

2.2.do-while循环

do-while循环与while循环的区别只在于,do-while循环是先执行循环体,然后再判断条件,这意味着循环体至少会被执行一次。

val i = 0
do {
  println("executed once")
} while( i != 0)

请注意
Scala中,赋值语句没有值,不像C和Java那样,因此在别的语言常见的while((c = getchar()) != ‘\n‘),Scala里面不能这样写。 这是因为,在Scala中,c = getchar(),虽然c会被赋值,但是该值不会赋给整个语句。这与其他语言不同,在C语言和Java中,c的值也会赋值给整个语句。

3.For语句

Scala的For语句跟其他语言差别很大,即使是有经验的程序员也需要仔细了解这部分内容。

在Scala中,没有形如for (initialize; test; update)的for循环,要实现相关功能,要么用while代替,要么,用Scala式的for语句for (i <- 1 to n)

3.1.用于集合的迭代

3.1.1.基本语法

用于迭代一个集合的for语句,格式为for(item <- collection),这有点像Java里的for (type var : arr),或者C#里的foreach(type var in arr)

由于Scala里创建一个集合的方式很多,所以for语句相对来说比较灵活,请看下面的示例。

package com.tv189.foundation

/**
 * Created by molyeo on 2015/7/30.
 */
object ForCondition extends App{
  val list=List("one","two","three","four");

  println("-------------use to-------------")
  for(i<-0 to list.length-1){
    print("  "+list(i));
  }

  println();
  println("-------------use until-------------")
  for(i<-0 until list.length){
  print("  "+list(i));
  }

  println();
  println("-------------use until-------------")
  for(item<-list){
    print("  "+item)
  }
}

请注意
请注意以上代码to和until的差别,to包含上边界,until则不包含。

3.1.2.嵌套for循环

Scala中,嵌套for循环比别的语言要容易,只要将表达式都放进for的括号里,用分号(;)隔开。比如:

for(i <- 0 to 1; j <- 1 to 2; k <- 2 to 3) {
    println("i = %d, j = %d, k = %d".format(i, j, k))
  }

以上代码,是i,j,k三个循环的嵌套。

3.1.3.条件for循环

for循环的循环器还可以带有条件语句,从而筛选循环的项目。比如:

for{i <- 0 to 5 
    if i % 2 == 0 
    j <- 1 to 2} {
  println("i = %d, j = %d".format(i, j))
}

以上代码中,i带有条件 i % 2 == 0,这意味着,仅有满足这个条件的i才会被循环执行。

3.2.中途绑定变量

请看下面的示例。

val list = List("Html", "XML", "JSON", "text", "md")
for{ ext <- list 
    lower = ext.toLowerCase 
    if lower != "html"} {
  println("Accepted data format: " + lower)
}

你可能已经发现了,上面代码中,toLowerCase在一个循环中可能会执行两次。这种情况我们希望能避免掉。 Scala的确也提供了这样的途径,你可以在for语句中途引入变量,然后在循环体内可以使用它。

上面代码中,我们引入了变量lower,这与通常我们定义变量的方式相比,只是少了val。这个变量在for表达式中和循环体中都可以使用。

3.3.用于产生新的集合

for语句,配合yield,可以用来产生新的集合。如下例所示。

val result =
  for(i <- 1 to 3; j <- 2 to 4)
  yield {
    i + j
  }
println(result)

上面代码产生了一个集合Vector(3, 4, 5, 4, 5, 6, 5, 6, 7)。

请注意
for-yield的格式是 for(claus) yield {body},不能将yield放到大括号内。

4.基本Match语句

Scala的Match语句功能基本上类似于其他语言中的switch-case,不过要强大很多。这一小节我们只介绍基本语法,关于“强大”的部分,留待模式匹配介绍。

用过switch-case语句的程序员知道,它的作用是让你在一系列选项中选择一个分支执行。mach与之类似,不过还是稍有差别。

"your selector" match {
  case "case 1" => //handle case 1     
  case "case 2" => //handle case 2
  ...
  case _ => //handle the rest, like default in switch-case
}

除了上述语法上的差别外,match与switch语句,有如下的不同点。

  1. 任意类型的常量或结果为常量的表达式都可以用于match语句。许多语言的switch只允许基本类型,Java则更严格,只允许整形和枚举类型。
  2. 每个分支不需要以break结束,因为Scala里没有贯穿执行(fall through)。
  3. 与if表达式一样,match表达式也有结果。这也是可以理解的,match其实是多个分支的if的另一种写法。

我们看一个实际的例子。

def score(grade: Char) = {
  grade match {
    case ‘A‘ => println("85~100")
    case ‘B‘ => println("70~84")
    case ‘C‘ => println("60~69")
    case ‘D‘ => println("45~60")
    case ‘E‘ => println("<45")
    case _ => println("Input error")
  }
}

def passed_?(grade: Char) = {
  grade match {
    case ‘A‘ | ‘B‘ | ‘C‘ => true
    case _ => false
  }
}

score(‘B‘)

if(passed_?(‘B‘)) println("Congrats!")
else println("Uh~ oh, we are sorry...")

函数score包含一个常规的match语句,这里我们能看到,每个分支只执行本分支的代码,没有贯穿。最后有一个case _处理未列出的情况。Scala中下划线(_)经常用作通配符。

函数passed_?有两处需要注意。一个是这个函数的返回类型是Boolean,由于该函数体只有一个match语句,显然其返回值就是match语句的值。match语句的值,其类型就是各个分支的共同类型。 如果这些类型不相同,就取最近的共同父类。

另一个值得注意的地方是,case ‘A‘ | ‘B‘ | ‘C‘ => true这正是其他语言中,switch-case贯穿执行的情况,类似于

case ‘A‘ :
case ‘B‘ :
case ‘C‘ : return true;

这也从一个侧面说明了,Scala里面,取消break和fall-through,能让代码更简洁,语义更清晰,而且让程序员不容易犯无心之过。

5.异常处理

Scala的异常处理与其他语言是一样的,功能一样,语法也只有细微的差别。异常机制的出现是为了处理程序中不正常的情况,比如要查找的文件不存在,数据库连接不上,网络中断等等。 当碰到异常情况时,方法抛出一个异常,终止方法本身的执行,异常传递到其调用者,调用者可以处理该异常,也可以升级到它的调用者。运行系统会一直这样升级异常,直到有调用者能处理它。 如果一直没有处理,则终止整个程序。

5.1.抛出异常

Scala中异常的抛出与Java一样,用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型,这与Java里一样。事实上,Scala的Throwable就是java.lang.Throwable的别名。

正如在底层类型Nothing里介绍过的那样,throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方。

def divide(x: Int, y: Int): Int = {
  if (y == 0) throw new Exception("Divide by zero")
  else x / y
}

以上代码divide方法的返回类型是Int,由于throw new Exception("Divide by zero")的类型是Nothing,是Int的子类,所以上述代码是正确的。

5.2.捕捉异常

异常捕捉的机制与其他语言中一样,如果有异常发生,catch字句是按次序捕捉的。因此,在catch字句中,越具体的异常越要靠前,越普遍的异常越靠后。 如果抛出的异常不在catch字句中,该异常则无法处理,会被升级到调用者处。

捕捉异常的catch子句,语法与其他语言中不太一样。在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case字句,如下例所示。

import java.io._
import java.lang.SecurityException

try {
  val file = new File("myfile")
  val stream = new FileInputStream(file)
  // use the stream
}
catch {
  case ex: FileNotFoundException => println("File not found")
  case ex: SecurityException => println("You don‘t have read access to the file")
  case _: Throwable => println("Other exception happened")
}

catch字句里的内容跟match里的case是完全一样的。由于异常捕捉是按次序,如果最普遍的异常,Throwable,写在最前面,则在它后面的case都捕捉不到,因此需要将它写在最后面。

5.3.finally字句

finally字句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作。如下面伪代码所示,Scala的finally跟其他语言是一样的。

val file = getFile
try {
  //use the file
}
catch {
  //handle exception
}
finally {
  //close the file no matter how above code goes
}

中断循环

Scala中没有break或continue关键字,这代表Scala不鼓励使用break或continue跳出循环。不过,如果实在想用的话,Scala提供了相应的类和方法。

使用break

如果确有需要,可以使用Breaks类的break方法,来实现退出循环的功能。如下例所示。

import scala.util.control.Breaks._

val list = List("functions.md", "menu.json", "index.html", "data.xml")

var found = false
breakable {
  for(item <- list) {
    if(item.endsWith("html")) {
      found = true
      break
    }
  }
}

if(found) 
  println("There is at least one html file in the list")
else 
  println("There is no html file in the list")

类似这样的代码,我们在指令式编程中,比如Java,C语言等,经常会看到。但是实际实现上还是有差别的。Scala的break是Breaks类的一个方法,所以,上述代码首先引入了Breaks类。

import scala.util.control.Breaks._

另外,break实际上是靠的抛出一个异常来实现的,源代码是

def break(): Nothing = { throw breakException }

这里,breakException是一个ControlThrowable类型的异常。因此,你的代码还需要用breakable包围起来。breakable的作用是捕获break抛出的异常,以免影响你真正的产品代码。其源代码类似于

def breakable(op: => Unit) {
    try {
      op
    } catch {
      case ex: BreakControl =>
        if (ex ne breakException) throw ex
    }
  }

在Scala里使用break真的不太方便,continue呢,则压根儿就没有,所以,咱最好还是别用了。这正是Scala的设计者所期待的,避免使用break和continue。

避免使用break和continue

避免使用break和continue完全是可以做到的。比较简单和符合我们编程固有习惯的方式,就是用while循环,将终止条件放到while字句里,比如上面的例子,要修改的话,可以将found放进while字句。

var found = false
while(!found) {
  for(item <- list) {
    if(item.endsWith("html")) {
      found = true
    }
  }
}

以上是一种可行的办法,但是,还不是Scala设计者希望看到的。他们希望看到的是程序员们能具有函数式编程的思想。还是拿上面的例子来说,从函数的角度考虑, 以上所需要完成的功能,用一个函数来表达,就是在一个字符串列表中寻找以html结尾的字符串,这个函数可以定义如下面的hasHtml。

val list = List("functions.md", "menu.json", "index.html", "data.xml")

def hasHtml(input: List[String]): Boolean = {
  input match {
    case Nil => false
    case x :: sub => {
      if(x.endsWith("html")) return true
      else hasHtml(sub)
    }
  }
}

if(hasHtml(list)) println("There is at least one html file in the list")
else println("There is no html file in the list")
hasHtml是比较容易理解的,如果list里面没有元素,当然是false,然后将list看成两部分,第一个元素,和剩余的子列表。 如果第一个元素是html的,那么就已经找到了,如果不是,则继续在子列表里面查找。这样的思考问题方式,比较容易理解和交流。 在子列表查找,和原始问题是一样的,只是输入集在缩小,这是一个典型的递归问题。事实上,函数式编程几乎离不开递归。 更特别的,以上hasHtml是一个尾递归,大部分函数式语言编译器都对尾递归有优化,Scala也不例外。对程序员来说,不用担心使用尾递归会带来性能下降。 关于尾递归,后续文章会进行讨论。

从指令式的思维转变到函数式,需要时间,不要强求瞬间完成。不过从现在开始,我们可以慢慢体会函数式编程的优雅。

 

参考文献:

http://meetfp.com/zh/scala-basic/break

 

[Scala基础系列 05]Scala控制结构

标签:

原文地址:http://www.cnblogs.com/molyeo/p/4720701.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!