if 表达式

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

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

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

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

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

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

while 循环

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

  1. def gcdLoop(x: Long, y: Long): Long = {
  2. var a = x
  3. var b = y
  4. while (a != 0) {
  5. val temp = a
  6. a = b % a
  7. b = temp
  8. }
  9. B
  10. }

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

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

  1. cala> def greet() { println("hi") }
  2. greet: ()Unit
  3.  
  4. scala> greet() == ()
  5. hi
  6. res0: Boolean = true

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

for 表达式

枚举集合类

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

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

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

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

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

过滤

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

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

  也可以这么写:

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

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

  1. for (
  2. file <- filesHere
  3. if file.isFile;
  4. if file.getName.endsWith(".scala")
  5. )println(file)

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

嵌套枚举

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

  1. def fileLines(file: java.io.File) =
  2. scala.io.Source.fromFile(file).getLines.toList
  3. def grep(pattern: String) =
  4. for {
  5. file <- filesHere
  6. if file.getName.endsWith(".scala")
  7. line <- fileLines(file)
  8. if line.trim.matches(pattern)
  9. } println(file + ": " + line.trim)
  10. grep(".*gcd.*")

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

mid-stream (流间) 变量绑定

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

  1. def grep(pattern: String) =
  2. for {
  3. file <- filesHere
  4. if file.getName.endsWith(".scala")
  5. line <- fileLines(file)
  6. trimmed = line.trim
  7. if trimmed.matches(pattern)
  8. } println(file + ": " + trimmed)
  9. grep(".*gcd.*")

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

制造新集合

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

  1. def scalaFiles =
  2. for {
  3. file <- filesHere
  4. if file.getName.endsWith(".scala")
  5. } yield file

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

  1. for {子句} yield {循环体}

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

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

使用 try 表达式处理异常

抛出异常

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

  1. throw new IllegalArgumentException

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

捕获异常

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

  1. import java.io.FileReader
  2. import java.io.FileNotFoundException
  3. import java.io.IOException
  4. try {
  5. val f = new FileReader("input.txt")
  6. // Use and close file
  7. } catch {
  8. case ex: FileNotFoundException => // Handle missing file
  9. case ex: IOException => // Handle other I/O error
  10. }

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

finally 子句

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

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

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

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

  调用 g() 产生 1 。

match 表达式

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

  1. val firstArg = if (args.length > 0) args(0) else ""
  2. firstArg match {
  3. case "salt" => println("pepper")
  4. case "chips" => println("salsa")
  5. case "eggs" => println("bacon")
  6. case _ => println("huh?")
  7. }

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

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

  1. val firstArg = if (!args.isEmpty) args(0) else ""
  2. val friend =
  3. firstArg match {
  4. case "salt" => "pepper"
  5. case "chips" => "salsa"
  6. case "eggs" => "bacon"
  7. case _ => "huh?"
  8. }
  9. println(friend)

变量范围

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

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

Scala 编程(四)内建控制结构的更多相关文章

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

    Scala的内建控制结构包括:if.while.for.try.match和函数调用 1.if表达式 //常见的写法 var filename = "name" if (!args ...

  2. Scala的内建控制结构

    Scala中的内建控制机构仅有if.while.for.try.match和函数调用.虽然Scala的基础控制结构少,但也足以支持指令式语言里所有的实质内容.因为它们都能产生值,有助于缩短代码. 程序 ...

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

    几乎所有的 Scala 的控制结构都会产生某个值.这是函数式语言所采用的方式,程序被看成是计算值的活动,因此程序的控件也应当这么做.另外,指令式语言经常具有三元操作符(如 C,C++和 Java 的? ...

  4. Scala 基础(6)—— 控制结构

    1. Scala 的内建控制结构 Scala 有几个内建的控制结构,包括: if 表达式 while 循环和 do-while 循环 for 表达式 try 表达式 match 表达式 Scala 的 ...

  5. scala-- 内建控制结构

    内建控制结构 ​ scala 内建的控制结构很少,只有 if while for try match 和函数调用 几种. 因为scala 从语法层面支持函数字面量.几乎所有的scala控制结构都会产生 ...

  6. Scala学习笔记(五):内建控制循环

    前言 Scala中内建控制循环包括if.while.for.try.match和函数调用. if和while与java类似,不做介绍. for 基础用法 def main(args: Array[St ...

  7. CODING DevOps 系列第四课:DevOps 中的质量内建实践

    什么是质量内建 随着时间的推移,我们项目的开发效率会逐渐降低,直到几年之后整个项目可能就无法维护,只能推倒重来.具体的表现首先就是随着时间推移,我们会发现整个需求列表里面能做的需求越来越少,因为每当我 ...

  8. javascript 对象初探 (四)--- 内建对象之旅之Array

     我们不要去纠结神马是内建对象,神马是內建构造器.到后来你们便会发现其实她们都是对象. Array()是一个构建数组的內建构造器函数: var arr = new Array(); 与下面的是等效的: ...

  9. Unity3d 网络编程(一)(Unity3d内建网络Network介绍)

    首先个人说说题外话,Unity3d使用的网络库核心是用C++实现的一款商业网络游戏引擎库. RakNet.所以对于移动设备来说,用Unity3d来写server是全然能够的,而且内建网络库的各项功能封 ...

随机推荐

  1. ios创建的sqlite数据库文件如何从ios模拟器中导出

    为了验证数据库的结构,有的时候需要使用一些管理工具来直接查看sqlite数据库的内容,在windows下有sqlite3的专用工具下载,而在ios下也可以使用火狐浏览器的插件sqlitemanager ...

  2. Android Studio学习随笔-基本事件(点击)

    最常见的点击事件有三种创建方法,在MainActivity.java的onCreate函数(在启动程序是优先运行的程序)中创建setOnClickListener(动态运行)(最常见) protect ...

  3. CDOJ 92 Journey(LCA&RMQ)

    题目连接:http://acm.uestc.edu.cn/#/problem/show/92 题意:给定一棵树,最后给加一条边,给定Q次查询,每次查询加上最后一条边之后是否比不加这条边要近,如果近的话 ...

  4. 【分享】w32service table XPsp2

    Ord   Address   fnAddr   Symbols-------------------------------- [  0] BF999280: BF93569A (win32k!Nt ...

  5. Overload和Override的区别?

    Overload和Override的区别? Override是重写:方法名称.参数个数,类型,顺序,返回值类型都是必须和父类方法一致的.它的关系是父子关系Overload是重载:方法名称不变,其余的都 ...

  6. Struts2里如何取得request,session,application

    第一种:取得MAP类型的request,session,application在java文件里写 package com.xjtu.st; import java.util.Map; import c ...

  7. jvm-初探

    目录 1,Java体系结构 2.jvm执行引擎 3,ClassLoader的体系结构 4,java class文件 概述 其实,学java不算新手了,但是却感觉很多基本的知识,我们一开始也许是记住而不 ...

  8. tomcat启动项目内存溢出问题

    catalina.bat文件的第二行加下面的即可: 注意最大内存设置,和系统的内存有关系 set JAVA_OPTS=%JAVA_OPTS% -Xms512m -Xmx1024m -XX:PermSi ...

  9. 【转】 iOS如何实现表格的折叠效果?

    原文 :  http://blog.csdn.net/youcanping2008/article/details/9202167 一.实现原理:就是在点击表格组头视图的时候,如果该表格视图的组展开了 ...

  10. Xcode7新特性

    更新Xcode7之后报错: Assertion failure in -[UIApplication _runWithMainScene:transitionContext:completion:], ...