scala编程(七)——内建控制结构

几乎所有的 Scala 的控制结构都会产生某个值。这是函数式语言所采用的方式,程序被看成是计算值的活动,因此程序的控件也应当这么做。另外,指令式语言经常具有三元操作符(如 C,C++和 Java 的?:操作符),表现得就像 if,却产生值。Scala 采用了这种三元操作符模型,但是把它称为 if。 换句话说,Scala 的 if 可以产生值。于是 Scala 持续了这种趋势让 for,try 和 match 也产生值。

if 表达式

Scala 的 if 如同许多其它语言中的一样工作。它测试一个状态并据其是否为真,执行两个分支中 的一个,并能返回分支中的值。

def main(args: Array[String]): Unit = {
    val filename =
      if (!args.isEmpty) args(0)
      else "default.txt"

    println(filename)
  }

if 有了两个分支:如果 args 不为空,那么初始化元素,args(0),被选中。否则,缺省 值被选中。这个 if 表达式产生了被选中的值,然后 filename 变量被初始化为这个值。这段代码更短一点儿,不过它的实际优点在于使用了 val 而不是 var使用 val 是函数式的风格,并能以 差不多与 Java 的 final 变量同样的方式帮到你。它让代码的读者确信这个变量将永不改变,节省了他们扫描变量字段的所有代码以检查它是否改变的工作。

使用 val 而不是 var 的第二点好处是他能更好地支持等效推论:equational reasoning在表达式没有副作用的前提下,引入的变量等效于计算它的表达式。因此,无论何时都可以用表达式替代变量名。如,要替代 println(filename),你可以这么写: println(if (!args.isEmpty) args(0) else "default.txt") 选择权在你。怎么写都行。使用 val 可以帮你安全地执行这类重构以不断革新你的代码。

尽可能寻找使用 val 的机会。它们能让你的代码既容易阅读又容易重构。

while 循环

while 和 do-while 结构被称为“循环”,不是表达式,因为它们不产生有意义的结果,结果的类 型是 Unit。说明产生的值(并且实际上是唯一的值)的类型为 Unit。被称为 unit value,写做 ()。 ()的存在是 Scala 的 Unit 不同于 Java 的 void 的地方。

  def main(args: Array[String]): Unit = {
    def greet() = println("hello")
    println(greet() == ())
  }

打印结果为:

hello
true

由于方法体之前没有等号,greet 被定义为结果类型为 Unit 的过程。因此,greet 返回 unit 值()。这被下一行确证:比较 greet 的结果和 unit 值()的相等性,产生 true。

另一个产生 unit 值的与此相关的架构,是对 var 的再赋值,它将返回()类型。

def main(args: Array[String]): Unit = {

    var content = ""
   println((content = StdIn.readLine()) == ())
   println((content = StdIn.readLine()) == ())
  }

这里的readLine函数并不会像Java那样返回从控制它读取到的内容,而是始终返回()即unit类型。

第一次输入hello,第二次输入一个空字符串,两次结果均为true;

for 表达式

Scala 的 for 表达式是为枚举准备的“瑞士军刀”。它可以让你用不同的方式把若干简单的成分组合来表达各种各样的枚举。简单的用法 完成如把整数序列枚举一遍那样通常的任务。更高级的表达式可以列举不同类型的多个集合,可以用任意条件过滤元素,还可以制造新的集合。

1)枚举集合类

 

def main(args: Array[String]): Unit = {
    val array = Array(1,2,3,4,5)
    for(elem <- array)
      println(elem)
  }

打印如下:

1
2
3
4
5

这样做,你的代码变 得更短并规避了许多枚举数组时频繁出现的超位溢出:off-by-one error。该从 0 开始还是从 1 开 始?应该加-1,+1,还是什么都不用直到最后一个索引?这些问题很容易回答,但也很容易答错。 还是避免碰到为佳。

2)过滤

有些时候你不想枚举一个集合类的全部元素。而是想过滤出一个子集。你可以通过把过滤器:filter: 一个 if 子句加到 for 的括号里做到。如果愿意的话,你可以包含更多的过滤器。只要不断加到子句里即可。如果在发生器中加入超过一个过滤器,if 子句必须用分号分隔。

  def main(args: Array[String]): Unit = {
    val array = Array(1,2,3,4,5,6,7,8,9,10)
    for(elem <- array if(elem>5);if(elem%2 == 0))
      println(elem)
  }

打印结果为:

6
8
10

3)制造新集合

你还可以创建一个值去 记住每一次的迭代。只要在 for 表达式之前加上关键字 yield。格式如下:

for(表达式) yield {表达式};最后返回yield表式的结果。

for 表达式完成的时候,结果将是一个包含了所有产生的值的集合。结果集合的类型基于枚举子句处理的集合类型。

示例如下:

def main(args: Array[String]): Unit = {
    val array = Array(1,2,3,4,5,6,7,8,9,10)
    val  elems: Array[String] =
      for(elem <- array if(elem>5);if(elem%2 == 0))
      yield elem.toString

    elems.foreach(println)
  }

打印结果:

6
8
10

使用 try 表达式处理异常

Scala 的异常和许多其它语言的一样。代之用普通方式那样返回一个值,方法可以通过抛出一个异常中止。方法的调用者要么可以捕获并处理这个异常,或者也可以简单地中止掉,并把异常升级到调用者的调用者。异常可以就这么升级,一层层释放调用堆栈,直到某个方法处理了它或没有剩下其它的方法。

1)抛出异常

异常的抛出看上去与 Java 的一模一样。首先创建一个异常对象然后用 throw 关键字抛出:

throw new IllegalArgumentException

Scala 里, throw 也是有结果类型的表达式

def main(args: Array[String]): Unit = {
    val n = 1;
    val half =
      if (n % 2 == 0)
        n / 2
      else
        throw new RuntimeException("n must be even")

    println("hello"+half)
  }

这里发生的事情是,如果 n 是偶数,half 将被初始化为 n 的一半。如果 n 不是偶数,那么在 half 能被初始化为任何值之前异常将被抛出。因此,无论怎么说,把抛出的异常当作任何类型的值都 是安全的。任何使用从 throw 返回值的尝试都不会起作用,因此这样做无害。

从技术角度上来说,抛出异常的类型是 Nothing。尽管 throw 不实际得出任何值,你还是可以把 它当作表达式。这种小技巧或许看上去很怪异,但像在上面这样的例子里却常常很有用。if 的一 个分支计算值,另一个抛出异常并得出 Nothing。整个 if 表达式的类型就是那个实际计算值的分支的类型。

打印结果如下:

Exception in thread "main" java.lang.RuntimeException: n must be even

这同时

说明:当异常未被捕获时,异常之后的代码不会被执行;

2)捕获异常

try-catch 表达式的行为与其它语言中的异常处理一致。程序体被执行,如果抛出异常,每个 catch 子句依次被尝试。本例中,如果异常是 FileNotFoundException,那么第一个子句将被执行。如果是IOException 类型,第二个子句将被执行。如果都不是,那么 try-catch 将终结并把异常上升出去。

def main(args: Array[String]): Unit = {

    try {
      val f = new FileReader("input.txt")
      // Use and close file
    } catch {
      case ex: FileNotFoundException => println("file not found ")// Handle missing file
      case ex: IOException => println("io error")// Handle other I/O error
    }
  }
}

打印结果如下:

file not found

Java 的一个差别是 Scala 里不需要你捕获检查异常:checked exception,或把它们声明在 throws 子句中

如果想让某些代码无论方法如何中止都要执行的话,可以把表达式放在 finally 子句里。如,你 或许想让打开的文件即使是方法抛出异常退出也要确保被关闭。

和其它大多数 Scala 控制结构一样,try-catch-finally 也产生值。如,代码展示了如何尝试拆分 URL,但如果 URL 格式错误就使用缺省值。结果是,如果没有异常抛出,则对应于 try 子句;如果抛出异常并被捕获,则对应于相应的 catch 子句。如果异常被抛出但没被捕获,表达式就没有返回值。由 finally 子句计算得到的值,如果有的话,被抛弃。通常 finally 子句做一 些清理类型的工作如关闭文件;他们不应该改变在主函数体或 try 的 catch 子句中计算的值。

 

object Demo8 {

  def urlFor(path: String) = {
    try {
      new URL(path)
    } catch {
      case e: MalformedURLException =>
        new URL("http://www.scalalang.org")
    }
  }


  def main(args: Array[String]): Unit = {

    val s = "abc";
    val url = urlFor(s)
    println(url)
  }
}

打印结果为:

http://www.scalalang.org

match 表达式

Scala 的匹配表达式允许你在许多可选项:alternative 中做选择,就好象其它语言中的 switch 语句。通常说来 match 表达式可以让你使用任意的模式:pattern 做选择。

 def main(args: Array[String]): Unit = {

    val firstArg = "salt"
    firstArg match {
      case "salt" => println("pepper")
      case "chips" => println("salsa")
      case "eggs" => println("bacon")
      case _ => println("huh?")
    }
  }

打印结果为:

pepper

Java 的 switch 语句比,匹配表达式还有一些重要的差别。其中之一是任何种类的常量,或其 他什么东西,都能用作 Scala 里的 case,而不只是 Java 的 case 语句里面的整数类型和枚举常量。 在这个例子里,可选项是字串。另一个区别是在每个可选项的最后并没有 break。取而代之,break 是隐含的,不会有从一个可选项转到另一个里面去的情况。这通常把代码变短了,并且避免了一 些错误的根源,因为程序员不再因为疏忽在选项里转来转去。

Java 的 switch 相比最显著的差别,或许是 match 表达式也能产生值。

 def main(args: Array[String]): Unit = {
    val firstArg = "salt"
    val friend =
      firstArg match {
        case "salt" => "pepper"
        case "chips" => "salsa"
        case "eggs" => "bacon"
        case _ => "huh?"
      }
    println(friend)
  }

打印结果为

pepper

 

猜你喜欢

转载自www.cnblogs.com/chxyshaodiao/p/12342288.html
今日推荐