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 编程(四)内建控制结构的更多相关文章

  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. Java基础知识强化之集合框架笔记05:Collection集合的遍历

    1.Collection集合的遍历 Collection集合直接是不能遍历的,所以我们要间接方式才能遍历,我们知道数组Array方便实现变量,我们可以这样: 使用Object[]  toArray() ...

  2. C++ | 调试 · 从汇编代码看i++和++i的区别

    以int类型为例 C++编译器为++i生成的代码是 013E1375 mov eax,dword ptr [i] 013E1378 add eax, 013E137B mov dword ptr [i ...

  3. Android中使用HttpGet和HttpPost访问HTTP资源

    需求:用户登录(name:用户名,pwd:密码) (一)HttpGet :doGet()方法//doGet():将参数的键值对附加在url后面来传递 public String getResultFo ...

  4. 在Linux上怎么安装和配置Apache Samza

    samza是一个分布式的流式数据处理框架(streaming processing),它是基于Kafka消息队列来实现类实时的流式数据处理的.(准确的说,samza是通过模块化的形式来使用kafka的 ...

  5. KMP算法总结

    kmp算法的T子字符串的下标的变化规律 大话数据结构这边书中的KMP算法的讲解跟最终的算法代码还是有很大的差别 java语言只会if判断语句,循环语句,但是这些语句以及可以包罗万象了,可以适用很多情况 ...

  6. JavaScript省市联动

    <html> <head> <title>JS省市二级联动菜单,整理收集.</title> </head> <body bgcolor ...

  7. iOS中的设计模式

    一. MVC MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离 ...

  8. JavaScript--循环--打印星星和99乘法表

    1.打印99乘法表 function chengfa(){ //反复调用公式 for(var r=1;r<=9;r++){ for(var i=1,str="";i<= ...

  9. C#防SQL注入代码的实现方法

    对于网站的安全性,是每个网站开发者和运营者最关心的问题.网站一旦出现漏洞,那势必将造成很大的损失.为了提高网站的安全性,首先网站要防注入,最重要的是服务器的安全设施要做到位. 下面说下网站防注入的几点 ...

  10. ARM9的MMU

    一 页表 1. 页表是放置在RAM(一般为DRAM)中的一个数据段. 2. ARM的地址空间为2^32字节,即4G字节. 3. 一级页表总共有4096条记录,每条记录对应的地址块为1M,一级页表中的记 ...