def gcdLoop(x: Long, y: Long): Long = {
var a = x
var b = y
while (a != 0) {
val temp = a
a = b % a
b = temp
2、do-while循环
同样,Scala也提供一个do-while循环结构,和上面这个不同的是,do-while循环首先执行循环体,然后判断循环条件是否满足,以决定下一次循环是否继续。下面这段代码,循环读取输入文本,直到遇到空行为止。
val line = ""
line = readLine()
print("Read: " + line)
} while (line != "")
3、Unit返回值
while和do-while都被称为循环,而不是表达式。这时由于while和do-while循环不会得到一个返回值,或者是它们的返回值为Unit。Unit值被写成(),如下所示,定义一个返回值为Unit的函数greet
def greet() {println("hi")}
greet() == ()
结果如下,表示greet函数的返回值与()是相等的。
需要注意的是,在Scala中对一个var变量重新赋值的语句得到的返回值也是Unit。
比如,如果执行下面这段代码,Scala编译器会认为将一个Unit类型的值与""
对比,结果永远为true
var line = ""
while ((line = readLine()) != "")
println("Read: " + line)
运行结果如下,即使某一次无任何输入,该循环仍然会执行并继续。
对于纯函数式编程语言来说,其中是没有while
和do-while
循环这一概念的。但是Scala提供了这一功能,while循环的使用,使得代码可读性更强。而如果不使用while循环的话,有些代码会写出递归调用的形式,比如下面采用递归的方法计算最大公约数。
def gcd(x: Long, y: Long): Long =
if (y == 0) x else gcd(y, x % y)
三、for表达式
for表达可以用于简单的字面变量以及集合类型变量,也可以在遍历时使用一些过滤条件进行过滤,还能够基于旧的集合生成新的集合对象。
1、枚举集合类型
下面这段代码,获取当前路径下所有的文件,并打印出来。其中filesHere变量是一个数组类型的变量,使用file <- filesHere
循环遍历数组中的每一个元素。
val filesHere = (new java.io.File(".")).listFiles
for (file <- filesHere)
println(file)
运行结果如下所示,
上面展示的是数组类型变量的for表达式,同样的for表达式还可以用于更多其他类型的变量遍历上。比如
for (i <- 1 to 4)
println("Iteration " + i)
2、增加过滤条件
也可以在for表达式中增加一个过滤条件,筛选出其中满足条件的元素进行处理。比如下面这段代码,也是列举出当前路径下的所有文件,但是只显示其中.scala类型的文件。
val filesHere = (new java.io.File(".")).listFiles
for (file <- filesHere if file.getName.endsWith(".scala"))
println(file)
结果如下:
在循环判断中,也可以增加多个判断条件。
for (
file <- filesHere
if file.isFile
if file.getName.endsWith(".scala")
) println(file)
3、嵌套循环
如果使用多个<-
表达式,就会得到一个嵌套循环结构。例如下面代码中有一个两层嵌套。外层循环遍历filesHere
遍历,内层循环遍历fileLines
方法读取到的文件中每一行的内容。
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
表达式的圆括号换成了花括号。
4、流间变量绑定
上面那段嵌套循环的代码中,重复执行了line.trim代码。如果不想重复的话,可以将line.trim
的结果通过=符合赋值给一个val变量,这个变量的val关键字可以省略不写。
在下面这段代码中,trimmed
变量在for表达式中被引入,并且初始化为line.trim
。接下来的代码中两次使用到了这个trimmed
变量,一次是在if表达式中,一次是在println
操作中。
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.*")
在原文中,这一小节的英语表述是(Mid-stream variable bindings)。 这里的流间变量绑定是指在for
表达式中引入的变量,可以在for
表达式的后续其他代码中使用该变量。
5、生成新的集合变量
之所以这里称for
为表达式,而不是for
循环,是因为for
表达式可以获得一个返回值。需要使用到关键字yield
。
(1)生成源集合相同类型的新集合
例如下面的代码,找出当前路径下所有的.scala类型的文件,并赋值给一个function。该for
表达式函数体每一次执行,都会产生一个file值。当filesHere
遍历完成之后,会将所有yield出来的file值存储到同一个集合类型对象中。返回集合的元素类型,与源集合中的元素类型相同。
def scalaFiles =
for {
file <- filesHere
if file.getName.endsWith(".scala")
} yield file
结果如下,
上面这段代码中需要注意的是yield关键字的位置。正确的格式应该是下面这样的,
for clauses yield body
由于上面代码中的函数体比较简单,看不出有什么异常,如果按照下面这种写法,就是错误的
for ( file <- filesHere if file.getName.endsWith(".scala") {
yield file
这种错误写法中,yield
关键字就是写入了函数体中。
(2)生成其他类型的集合
其实在Scala的for表达式中,也可以生成与源集合不同类型的集合。比如下面这段代码中,首先获取当前路径下的所有.scala文件,然后对每一个文件读取其中的所有行,去重,然后取出其中只包含for
关键字的代码,最后,获取这几行代码的字符个数。
其中的fileLines
函数得到一个Iterator[String]
类型的返回值。
val forLineLengths =
for {
file <- filesHere
if file.getName.endsWith(".scala")
line <- fileLines(file)
trimmed = line.trim
if trimmed.matches(".*for.*")
} yield trimmed.length
总结一下,有关于for
表达式,可以很方便的遍历某个集合,也可在遍历时加入过滤条件对循环的集合进行筛选。同时也可以在for
表达式中多次写入<-
实现嵌套循环,并且,for
表达式中引入的变量可以省略val
关键字并在后续多次使用。最后,面对复杂的for
表达式时,最好不用圆括号,而改为使用花括号。
四、try表达式异常处理
Scala中的表达式,除了正常执行并得到一个返回值的情况外,还有一种是运行时出现异常。出现异常时,程序可能会被终止,也可以由方法调用者捕获并对该异常作出处理。
1、抛出异常
Scala抛出异常和Java类似,同样适用throw
关键字,抛出的这个异常类,也可以由开发者自定义。
throw new IllegalArgumentException
对于throw
表达式,Scala其实也对应了一个返回值,其类型为Nothing。
比如下面求一个偶数的一半,当传入的n
是偶数时才进行计算,否则直接抛出一个RuntimeException。
val half = (
if (n % 2 == 0)
n / 2
throw new RuntimeException("n must be even"))
运行结果如下,当n为3时,直接抛出一个异常,而当n为4时,返回一个Int值2。
前面提到,if
表达式返回值的类型为两个分支计算值的公共父类,而现在throw
表达式的的返回值为Nothing
,并且在Scala的类层级关系中Nothing
是任何类的直接或间接子类。也就是说,对于if
表达式来说,某一个分支是throw
表达式的话,那么该if
表达式最终返回值的类型是有值计算的那一个分支的类型。
2、捕获异常
抛出了异常,如果不加处理,程序就会终止。抛出异常后,可以使用catch
关键字捕获该异常,并针对不同的异常作出不同的处理方案。下面的代码展示了一个完成的try...catch
代码结构
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
try {
val f = new FileReader("input.txt")
} catch {
case ex: FileNotFoundException =>
case ex: IOException =>
当抛出异常时,会顺序执行catch
代码块中的逻辑。上面代码中,如果抛出FileNotFoundException
,那么就会执行文件不存在这一分支的代码。而如果抛出IOException
异常,就会执行I/O异常这一部分的逻辑。而如果这两种异常都匹配不到抛出的异常时,程序还是会终止。
3、finally子句
类似于Java代码中,有时候遇到抛出异常而导致程序终止时,可能还希望执行一些收尾的逻辑。比如下面这段代码中,如果读取文件时抛出异常导致程序终止,那么最好在终止前将打开的文件关闭。关闭文件的这段逻辑就写在finally
子句中。
import java.io.FileReader
val file = new FileReader("input.txt")
try {
} finally {
file.close()
4、产生一个返回值
类似于其他的控制结构,try-catch-finally
代码也会产生一个返回值。
比如下面代码中,对一个URL地址进行处理,如果不抛出异常,那么使用传入的地址构造一个URL对象。如果抛出异常并被捕获到的话,则使用默认地址构造一个URL对象。
import java.net.URL
import java.net.MalformedURLException
def urlFor(path: String) =
try {
new URL(path)
} catch {
case e: MalformedURLException =>
new URL("http://www.scala-lang.org")
如果抛出异常并没有被捕获到,那么返回的结果为Nothing
。而finally
子句中设置的返回值是无法获取到的,这也是由于程序终止时一般在finally
中只需要做一些收尾的操作,而不需要去改变某些变量的值。
如果在finally
中显示的return
了某个返回值,或者抛出一个异常,那么这个返回值或者抛出的异常会将之前try-catch
中应有的返回值覆盖掉。比如
def f(): Int = try {return 1} finally {return 2}
结果如下:
而不显示返回一个值时,最终的返回值为try中的内容。比如
def g(): Int = try {1} finally {2}
结果如下:
五、match表达式
match
表达式类似于switch
表达式,可以在多个待选值中进行选择。
看下面的代码,如果args
的长度大于0,那么firstArg
变量的值为第一个元素,否则为空字符串。然后,根据firstArg
变量的具体内容,如果是salt
则打印paper
,如果是eggs
则打印bacon
。而如果不是这三种中的任何一个,则打印huh?
。下面代码中的下划线_
表示任意匹配。
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中的switch
不同的是,Scala中的match
语法可以匹配包括整数,字符串等任意类型的变量,并且在每个case
后没有break
关键字。而switch
和match
最大的不同是,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)
可以从下面的结果看到,friend
的值为salsa
。
BODY { FONT-FAMILY:Tahoma; FONT-SIZE:10pt }
P { FONT-FAMILY:Tahoma; FONT-SIZE:10pt }
DIV { FONT-FAMILY:Tahoma; FONT-SIZE:10pt }
TD { FONT-FAMILY:Tahoma; FONT-SIZE:10pt }
programing in scala 学习笔记
* 使用scala解释器 类似于脚本编程
* 定义的变量和函数会暂存在解释器中(就像解释器是一个大的object 写的代码会实时编译到里面)
* 使用函数会将返回值显示在下一行
* $ scala
object Part1 {
* 变量定义
1. java通过private可以使类内部方法私有化,对外不可见。Scala除了private方式,还可以使用本地函数(内嵌在函数中的函数)实现,本地函数仅在包含它的函数代码块中可见,外部无法访问。在作用域方面,本地函数可以访问包含它的外层函数的入参,不需要再传入参数。
2. 函数是Scala的头等函数(first-class function)或头等结构,不仅可以像java那样定义和
第二课细读ScalaSecondWIthProgrammingInScala结合之前的学习和感想 ,顺序细读一遍Scala的书 ,整理
为了方便查看和管理代码 ,书中包含的脚本/Scala解释器部分我都用mian函数去测试 ,方便记录
当然Scala的解释器和脚本运行方式也是要去尝试一下的 ,也是很特别的
利用这段空闲时间阅读完了Programming In Scala中文版的书 ,对Sc
1. Scala面向对象与函数编程(函数是一级的first class;函数输入输出映射,数据immutable不可修改)结合,兼容、简洁、高级抽象、静态类型(类型推断使Scala具有动态语言的赋值形式,但Scala变量保存、赋值,编译特征决定它是静态语言)、灵活。
2. 函数式编程风格,尽量尝试不使用任何var变量,使用val变量编程。
3. java里需要写public的地
Programming in Scala is the definitive book on Scala, the new language for the Java Platform that blends object-oriented and functional programming concepts into a unique and powerful tool for developers.Coauthored by the designer of the Scala language, this authoritative book will teach you, one step at a time, the Scala language and the ideas behind it.The book is carefully crafted to help you learn. The first few chapters will give you enough of the basics that you can already start using Scala for simple tasks. The entire book is organized so that each new concept builds on concepts that came before - a series of steps that promises to help you master the Scala language and the important ideas about programming that Scala embodies.A comprehensive tutorial and reference for Scala, this book covers the entire language and important libraries.