Scala 编程内建控制结构

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala 编程内建控制结构相关的知识,希望对你有一定的参考价值。

if 表达式

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

var filename = "default.txt"
if (!args.isEmpty)
  filename = args(0)

  由于 Scala 的 if 是能返回值的表达式,可以改成用 val 的更函数式的风格:

val filename =
  if (!args.isEmpty) args(0)
  else "default.txt"

  使用 val 而不是 var 的第二点好处是他能更好地支持等效推论:equational reasoning。在表达式没有副作用的前提下,引入的变量等效于计算它的表达式。因此,无论何时都可以用表达式替代变量名:

println(if (!args.isEmpty) args(0) else "default.txt")

 

while 循环

  Scala 的 while 循环表现的和在其它语言中一样。包括一个状态和循环体,只要状态为真,循环体就一遍遍被执行:

def gcdLoop(x: Long, y: Long): Long = {
  var a = x
  var b = y
  while (a != 0) {
    val temp = a
    a = b % a
    b = temp
  }
  B
}

  Scala 也有 do-while 循环。除了把状态测试从前面移到后面之外,与 while 循环没有区别。

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

cala> def greet() { println("hi") }
greet: ()Unit

scala> greet() == ()
hi
res0: Boolean = true

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

 

for 表达式

枚举集合类

  用 for 做的最简单的事情就是把一个集合类的所有元素都枚举一遍:

val filesHere = (new java.io.File(".")).listFiles
for (file <- filesHere)
  println(file)

  代码先创建指向当前目录 “.” 的文件,调用 listFiles 方法,返回 File 对象数组保存在 filesHere 变量中,通过发生器:generator的语法“file <- filesHere”遍历了 filesHere 的元素,每一次枚举 file 的新 val 就被元素值初始化并被打印出来。

  for 表达式语法对任何种类的集合类都有效而不只是数组,可以使用类似于 “1 to 5” 这样的语法创建一个 Range ,然后用 for 枚举:

scala> for (i <- 1 to 4)
     | println("lteration" + i)
lteration1
lteration2
lteration3
lteration4

 

过滤

  有些时候不想枚举一个集合类的全部元素。而是想过滤出一个子集。可以通过把过滤器:filter:一个 if 子句加到 for 的括号里做到:

val filesHere = (new java.io.File(".")).listFiles
for (file <- filesHere if file.getName.endsWith(".scala")) 
  println(file)

  也可以这么写:

for (file <- filesHere)
  if (file.getName.endsWith(".scala"))
  println(file)

  如果愿意的话,可以包含更多的过滤器。只要不断加到子句里即可:

for ( 
  file <- filesHere
  if file.isFile;
  if file.getName.endsWith(".scala")
)println(file)

  如果在发生器中加入超过一个过滤器, if 子句必须用分号分隔。

 

嵌套枚举

  如果加入多个 <- 子句就得到嵌套的“循环”:

def fileLines(file: java.io.File) = 
  scala.io.Source.fromFile(file).getLines.toList 
def grep(pattern: String) =
  for { 
    file <- filesHere
    if file.getName.endsWith(".scala") 
    line <- fileLines(file) 
    if line.trim.matches(pattern)
    } println(file + ": " + line.trim)
grep(".*gcd.*")

  代码展示的 for 表达式有两个嵌套循环,外层循环枚举 filesHere ,内层的枚举所有以 .scala 结尾的文件的 fileLines(file) 。可以使用大括号代替小括号环绕发生器和过滤器,这样的好处是可以省略一些使用小括号必须加的分号。

 

mid-stream (流间) 变量绑定

  注意到前段代码中重复出现的表达式 line.trim ,这不是个可忽略的计算,如果想每次只算一遍,可以用等号 (=) 把结果绑定到新变量做到这点,绑定的变量被当做用 val 引入和使用,不过不用带关键字 val :

def grep(pattern: String) =
  for { 
    file <- filesHere
    if file.getName.endsWith(".scala") 
    line <- fileLines(file) 
    trimmed = line.trim
    if trimmed.matches(pattern)
    } println(file + ": " + trimmed)
grep(".*gcd.*")

  名为 trimmed 的变量被引入 for 表达式,并被初始化为 line.trim 的结果值。之后的 for 表达式就可以在两个地方使用这个新变量,一次在 if 中,一次在 println 中。

 

制造新集合

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

def scalaFiles =
  for {
    file <- filesHere
      if file.getName.endsWith(".scala")
    } yield file

  for 表达式在每次执行的时候会制造一个值,当 for 表达式完成的时候,结果将是一个包含了所有产生的值的集合,结果集合的类型基于枚举子句处理的集合类型。 for-yield 表达式的语法:

for {子句} yield {循环体}

  yield 在整个循环体之前,即使循环体是一个被大括号包围的代码块,也一定把 yield 放在左括号之前而不是代码的最后一个表达式之前:

for (file <-filesHere if file.getName.endsWith(".scala")) {
  yield file // 语法错误! 
}

 

使用 try 表达式处理异常

抛出异常

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

throw new IllegalArgumentException

  Scala 里, throw 也是有结果类型的表达式,抛出异常的类型是 Nothing ,尽管 throw 不实际得出任何值,但还是可以把它当做表达式。

 

捕获异常

  用来捕获异常的语法如下:

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException 
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
}

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

 

finally 子句

  和其它大多数 Scala 控制结构一样, try-catch-finally 也产生值。 Scala 的行为与 Java 的差别仅源于 Java 的 try-finally 不产生值。 Java 里如果 finally 子句包含一个显式返回语句,或抛出一个异常,这个返回值或异常将“凌驾”于任何之前源于 try 代码块或某个它的 catch 子句产生的值或异常之上:

def f(): Int = try { return 1 } finally { return 2 }

  调用 f() 产生结果值 2 ,相反:

def g(): Int = try { 1 } finally { 2 }

  调用 g() 产生 1 。

 

match 表达式

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

val firstArg = if (args.length > 0) args(0) else "" 
firstArg match {
  case "salt" => println("pepper")
  case "chips" => println("salsa")
  case "eggs" => println("bacon")
  case _ => println("huh?")
}

  match 缺省情况用下划线 (_) 说明,这是常用在 Scala 里作为占位符表示完全不清楚的值的通配符。

  Scala 里的 case 匹配表达式可以使任何种类的常量,每个可选项最后没有 break ,但是 break 是隐含的。 match 表达式也能产生值:

val firstArg = if (!args.isEmpty) args(0) else ""
val friend =
  firstArg match { 
    case "salt" => "pepper"
    case "chips" => "salsa"
    case "eggs" => "bacon"
    case _ => "huh?"
  }
println(friend)

 

变量范围

  Scala 程序里的变量定义有一个能够使用的范围:scope。大括号通常引入了一个新的范围,任何定义在大括号里的东西在括号之后就脱离了范围。

  一旦变量被定义了就不能在同一范围内定义同样的名字,但是可以在内部范围内定义与外部范围内名称相同的变量,用大括号括起来即为内部范围。内部变量会遮蔽同名的外部变量。

 

以上是关于Scala 编程内建控制结构的主要内容,如果未能解决你的问题,请参考以下文章

Scala学习笔记——内建控制结构

Scala的内建控制结构

内建控制结构

Spark基础学习笔记12:Scala内建控制结构

大数据处理学习笔记1.5 掌握Scala内建控制结构

每天半小时掌握Scala(day 02)