Scala的内建控制结构
Scala中的内建控制机构仅有if、while、for、try、match和函数调用。虽然Scala的基础控制结构少,但也足以支持指令式语言里所有的实质内容。因为它们都能产生值,有助于缩短代码。
程序员可以通过使用返回值的控制结构简化代码,避免创建临时变量来保存控制结构中的计算结果。
1 If表达式
1.1.常规式
var filename="default"
if(!args.isEmpty)
filename=args(0)
1.2.scala里根据条件做初始化的例子
val filename=
if(!args.isEmpty) args(0)
else "default.txt"
这样写有两个好处:
一是使用val体现了函数式风格,并具有java的final变量类似的效果。它告诉读者,代码变量将不再改变,从而节省了他们审查变量作用域的所有代码和检查它是否改变的工作。
二是更好地支持等效推论。在表达式没有副作用的前提下,引入的变量等效于计算它的表达式。无论何时都可以用表达式替换变量名。
尽可能使用val,它能让你的代码既容易阅读又容易重构。
2.While循环
2.1常规式
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
}
例如Do-While循环
var line =""
do {
line=readLine()
println("Read: "+line)
} while(line!="")
while和do-while结构之所以称之为“循环”,而不是表达式,因为他们不能产生有意义的结果,结果类型只能是Unit类型,是表明存在且唯一存在类型为Unit的值,写成()。()的存在是scala的Unit与Java的void不同的地方,例如
def greet() {println("hi")}
greet()==()
以上代码将返回true。因为greet返回unit值()。所有greet的结果和unit值()相等,返回true。
另外需要注意的是,赋值等式本身也是unit值
例如var line=""
while((line=readLine())!="") //不起作用
println("Read: "+line)因为赋值语句line=readLIne()的值将永远返回()而不是”“
建议你在代码中更为审慎地使用while循环。如果对while或do循环没有非用不可的理由,请尝试别的方式实现同样的功能。
3.For表达式
scala的For表达式是枚举操作的瑞士军刀。
3.1枚举集合类
val filesHere=(new java.io.File(".")).listFiles
for(file<- filesHere)
println(file)
通过被称为发生器语法“file<-filesHere”,遍历了filesHere数组的元素。
for表达式语法对任何种类的集合类都有效,而不只是数组。例如Range
for(i<-1 to 4)
println("Iteration "+i)
以下这种方式遍历数组不常用:
for(i<-0 to filesHere.length-1)
println(filesHere(i))
不常用的原因是由于集合本身可以直接被枚举,并且不会出现溢出。
3.2 过滤
又是不想枚举处全部元素,只想过滤出某个子集。这可以通过for表达式的括号中添加过滤器(filter),即if子句。例如过滤以.scala为结尾的文件名:
val filesHere = (new java.io.File(".")).listFiles
for(file<-filesHere if file.getName.endsWith(".scala"))
println(file)
也可以加入更多的过滤器,只要不断添加if子句即可。如果发生器中超过一个过滤器,if子句必须用分号分隔。
for(
file<-fileHere
if file.isFile;
if file.getName.endsWith(".scala")
) println(file)
3.3嵌套枚举
如果加入多个<-子句,就可以嵌套循环。例如:
def fileLines(file:java.io.File)=
scala.io.Source.fromFile(file).getLines.toList def grep(pattern:String)=
for(
file<-fileHere
if file.getName.endsWith(".scala")
line<-fileLines(file)
if line.trim.mathches(pattern)
) println(file+": "+line.trim) grep(".*gcd.*")
3.4 流间变量绑定
在前面的代码中重复出现了表达式line.trim。如果想要只计算一遍,可以通过等号=把结果绑定到新的变量。绑定变量被当做val引入和使用,但是不带关键字val。
def grep(pattern:String)=
for(
file<-fileHere
if file.getName.endsWith(".scala")
line<-fileLines(file)
trimmed=line.trim
if trimmed.mathches(pattern)
) println(file+": "+trimmed) grep(".*gcd.*")
3.5 制造新的集合
到现在为止所有例子只是对枚举值进行操作然后释放,除此以为,还可以创建一个值去记住每一次的迭代,只要在for表达式之前加上关键字yield。比如:
def scalaFiels=
for{
file<-filesHere
if file.getName.endsWith(".scala")
} yield file
for表达式在每次执行的时候都会产生一个新值,本例中是file。当for表达式完成的时候,结果将是包含了产生值的集合对象。对象的类型基于枚举子句处理的集合类型。本例中的结果为Array[File],因为filesHere是数组并且产生的表达式类型是File。
另外注意yield一定放在左括号之前,而不是代码块的最后一个表达式之前。
for {子句} yield {循环体}
val forLineLengths=
for {
file<- filesHere
if file.getName.endsWith(".scala")
line<-fileLines(file)
trimmed = line.trim
if trimmed.matches(".*for.*")
} yield trimmed.length
4使用try表达式处理异常
Scala中方法处理能返回值以外,还可以通过抛出异常中止执行。方法的调用者要么可以捕获并处理这个异常,或者也可以简单的终止掉,并把异常上升到调用者的调用者处。
4.1抛出异常
throw也是有结果类型的表达式,例如:
val half=
if (n%2==0)
n/2
else
throw new RuntimeException("n must be even")
异常抛出的类型是Nothing。
像这样的例子经常使用。if的一个分支计算值,另一个爆出异常并得出Nothing。
4.2捕获异常
在scala中捕获异常时,在catch子句中经常使用case模式匹配,这是scala的特色。模式匹配是一种很强大的特征。
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
try{
val f= new FileReader("inpit.txt")
} catch{
case ex:FileNotFoundException=>//处理丢失的文件
case ex:IOException=> //处理其他的IO错误
Scala里不需要捕获检查异常,或者把他们生命在throws子句中。
4.3 finally子句
如果要让某些代码无论如何都要执行的话,可以放在finall子句中。例如:
import java.io.FileReader
val file=new FileReader("input.txt")
try{
//使用文件
}finally {
file.close() //确保关闭文件
}
在Scala中还可以使用另一种被称为出借模式(loan pattern)的技巧更简洁地达到同样的目的。
def withPrintWriter(file:File,op:PrintWriter=>Unit) {
val writer= new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
上面的代码中,由withPrintWriter而并非用户代码,确认文件在结尾被关闭。因为不可能忘记关闭文件。因为控制抽象函数,打开了资源并借贷给函数。当用户函数完成时,它发出信号说明它不再需要借的资源,于是资源被关闭在finally中,以确信其确实被关闭。
4.4生成值
try-catch-finally也产生值,但是finally子句应当做关闭文件之类的清理工作,他们不应该修改主函数体或catch子句中计算的值。
scala与java的try-finally最大的不同是,在java中finally子句不产生值。
def f():Int=try{return 1} finally {return 2}
def g():Int=try{1} finally{2}
调用f()返回2,调用g()返回1。
5.匹配表达式
scala中的match类似于java中的swith,但是他可以在提供多个备选项中做出选择。默认情况下用_说明。例如:
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?")
与Java相比,Scala的匹配表达式还有一些重要差别。
(1)任何类型的常量,或者其他东西都可以成为case,而不只是java中的整数类型和枚举常量。
(2)每个备选项的最后没有break,因为break是隐含的。
(3)match也能产生值。例如:val firstArg = if(!args.isEmpty) args(0) else "" val firend= firstArg match{
case "salt"=> println("pepper")
case "chips"=> println("salsa")
case "eggs"=> println("bacon")
case _ =>println("huh?")
}
println (frined)
Scala的内建控制结构的更多相关文章
- Scala学习笔记——内建控制结构
Scala的内建控制结构包括:if.while.for.try.match和函数调用 1.if表达式 //常见的写法 var filename = "name" if (!args ...
- Scala 编程(四)内建控制结构
if 表达式 Scala 的 if 如同许多其它语言中的一样工作.它测试一个状态并据其是否为真,执行两个分支中的一个: var filename = "default.txt" i ...
- scala编程(七)——内建控制结构
几乎所有的 Scala 的控制结构都会产生某个值.这是函数式语言所采用的方式,程序被看成是计算值的活动,因此程序的控件也应当这么做.另外,指令式语言经常具有三元操作符(如 C,C++和 Java 的? ...
- Scala学习笔记(五):内建控制循环
前言 Scala中内建控制循环包括if.while.for.try.match和函数调用. if和while与java类似,不做介绍. for 基础用法 def main(args: Array[St ...
- scala-- 内建控制结构
内建控制结构 scala 内建的控制结构很少,只有 if while for try match 和函数调用 几种. 因为scala 从语法层面支持函数字面量.几乎所有的scala控制结构都会产生 ...
- BASH BUILTIN COMMANDS 内建命令
除非另外说明,这一章介绍的内建命令如果接受 - 引导的选项,那么它也接受 -- 作为参数,来指示选项的结束 : [arguments] 没有效果:这个命令除了扩展 arguments 并且作任何指定的 ...
- Bash shell的内建命令:type
type指令是用来观察指令时来自于外部指令还是内建在bash中的指令. type [-tpa] name 选项与参数: :不加任何选项与参数时,type会显示出name是外部指令还是bash内建指 ...
- Bash Shell内建命令和保留字
Bash Shell内建命令和保留字命令含义!保留字,逻辑非:不做任何事,只做参数展开.读取文件并在shell中执行它alias设置命令或命令行别名bg将作业置于后台运行bind将关键字序列与read ...
- Python内建的对象列表
Python内建的对象列表 刚写Python肯定会遇到这样的情况,想写些什么,但又不知从何写起... 在我看来问题在于我们不知道有什么东东可以拿来玩,这里列出Python的内建对象,稍微归类了一下,多 ...
随机推荐
- vim 的编辑模式 命令模式
1.vim的编辑模式 进入编辑模式 按键: a i o a: 表示在光标当前的,后面开始插入,写数据 i : 则表示 前面 . o : 表面在光标当前的,下一行开始写入数据. O : 大写的 ...
- 2018.4.3 配置AD服务器步骤
net ads命令表 配置AD服务器步骤:1. 安装rpm依赖包yum -y install pam_krb5* krb5-libs* krb5-workstation* krb5-devel* kr ...
- djkstra nlogn
#include<bits/stdc++.h> #define fi first #define se second #define pii pair<int,int> usi ...
- Redis源码剖析和注释(七)--- 快速列表(quicklist)
Redis 快速列表(quicklist)1. 介绍quicklist结构是在redis 3.2版本中新加的数据结构,用在列表的底层实现. 通过列表键查看一下:redis 列表键命令详解 127.0. ...
- MySQL Binlog--binlog_format参数
===================================================================================== binlog_format参 ...
- YAML Class ID Reference
Classes Ordered by ID Number ID Class 1 GameObject 2 Component 3 LevelGameManager 4 Transform 5 Time ...
- ML(5)——神经网络2(BP反向传播)
上一章的神经网络实际上是前馈神经网络(feedforward neural network),也叫多层感知机(multilayer perceptron,MLP).具体来说,每层神经元与下一层神经元全 ...
- MySQL 5.7新特性之在线收缩undo表空间
1. MySQL 5.5时代的undo log 在MySQL5.5以及之前,大家会发现随着数据库上线时间越来越长,ibdata1文件(即InnoDB的共享表空间,或者系统表空间)会越来越大,这会造成2 ...
- Apache 配置Https 转发Tomcat Http
Apache 相对于nginx的配置对比起来相当复杂啦,朋友之前的系统使用的是Apache需要增加一个虚拟主机,主要配置从Apache转发Tomcat. 首先需要拆解下步骤: Apache 支持Htt ...
- ASP.NET AJAX入门系列(1):概述
经常关注我的Blog的朋友可能注意到了,在我Blog的左边系列文章中,已经移除了对Atlas学习手记系列文章的推荐,因为随着ASP.NET AJAX 1.0 Beta版的发布,它们已经不再适用,为了不 ...