一、Scala函数闭包
函数闭包:
-
我们介绍的函数都只引用到传入的参数,假如我们定义入如下的函数:
-
(x:Int)=> x + more
-
这里我们引入一个自由变量more。它不是所定义的函数的参数,而这个变量定义在函数的外面,比如:
-
var more = 1
那么我们有如下的结果:
scala> var more = 1
more: Int = 1
scala> val addMore = (x:Int) => x + more
addMore: Int => Int = <function1>
scala> addMore(100)
res33: Int = 101
-
这样定义的函数变量addMore成为一个"闭包",因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
-
有意思的是:当这个自由变量发生变化时,Scala的闭包能够捕获到这个变化,所以Scala的闭包捕获的是变量本身而不是变量的值。
scala> more = 9999
more: Int = 9999
scala> addMore(100)
res34: Int = 10099
同样的,如果变量在闭包内发生变化,也会反映到函数外面定义的闭包的值,比如
scala> val someNumbers = List(-11,-10,5,0,5,10)
someNumbers: List[Int] = List(-11, -10, 5, 0, 5, 10)
scala> var sum = 0
sum: Int = 0
scala> someNumbers.foreach(sum += _)
scala> sum
res38: Int = -1
-
我们可以看到在闭包中修改sum的值,其结果还是传递到闭包的外面。
-
如果一个闭包所访问到的变量有几个不同的版本,比如一个闭包使用了一个函数的局部变量(参数),然后这个函数调用很多次,那么所定义的闭包应该使用所引用的局部变量的哪个版本呢?简单的说,该闭包定义所引用的变量为定义该闭包是变量的值,也就是定义闭包是相当于保存了当时程序状态的一个快照。比如我们定义下面一个函数闭包:
scala> def makeIncreaser(more:Int) = (x:Int) => x + more
makeIncreaser: (more: Int)Int => Int
scala> val inc1=makeIncreaser(1)
inc1: Int => Int = <function1>
scala> val inc9999=makeIncreaser(9999)
inc9999: Int => Int = <function1>
scala> inc1(10)
res39: Int = 11
scala> inc9999(10)
res40: Int = 10009
- 当我们调用makeIncreaser(1)时,创建了一个闭包,该闭包定义时more的值为1,而调用makeIncreaser(9999)所创建的闭包的more的值为9999。此后你也无法修改已经返回的闭包的more的值。因此inc1始终为加1,而inc9999始终为加9999。
二、Scala正则表达式
- Scala通过scala.util.matching包中的Regex类来支持正则表达式。以下实例演示了使用正则表达式查找单词Scala
import scala.util.matching.Regex
object Test{
def main(args: Array[String]) {
val pattern = "Scala".r
val str = "Scala is Scalable and cool"
println(patern findFirstIn str)
}
}
- 执行以上代码:
- 输出结果:Some(Scala)
实例中使用String类的r()方法构造了一个Regex对象。
然后使用findFirstIn方法找到首个匹配项。
如果需要查看所有的匹配项可以使用findAllIn方法。
你可以使用mkString()方法来连接正则表达式匹配结果的字符串,并可以使用管道(|)来设置不同的模式:
import scala.util.matching.Regex
object ChangeLongApp {
def main(args: Array[String]) {
val pattern = new Regex("(S|s)cala") //首字母可以是大写S或小写s
val str = "Scala is scalable and cool"
println((pattern findAllIn str).mkString(",")) //使用逗号,连接返回结果
}
}
输出:Scala,scala
如果需要将匹配的文本替换为指定的关键字,可以使用replaceFirstIn()方法来替换第一个匹配项,使用replaceAllIn()方法来替换所有匹配项,实例如下:
import scala.util.matching.Regex
object ChangeLongApp{
def main(args: Array[String]) {
val pattern = "(S|s)cala".r
val str = "Scala is scalable and cool"
println(pattern replaceFirstIn(str,"java"))
}
}
- 输出结果如下:java is scalable and cool
正则表达式:
Scala的正则表达式继承了Java的语法规则,Java则大部分使用了Perl语言的规则。
下表我们给出了常用的一些正则表达式规则:
表达式 | 匹配规则 |
---|---|
^ | 匹配输入字符串开始的位置 |
$ | 匹配输入字符串结尾的位置 |
. | 匹配除"\r\n"之外的任何单个字符 |
[…] | 字符集。匹配包含的任一字符。例如,"[abc]“匹配"plain"中的"a”. |
[^…] | 反向字符集。匹配未包含的任何字符。例如,"[^abc]“匹配"plain"中"p”,“l”,“i”,“n”. |
\A | 匹配输入字符串开始的位置(无多行支持) |
\z | 字符串结尾(类似$,但不受处理多行选项的影响) |
\Z | 字符串结尾或行尾(不受处理多行选项的影响) |
re* | 重复零次或更多次 |
re+ | 重复一次或更多次 |
re? | 重复零次或一次 |
re{ n } | 重复n次 |
re{ n, } | - |
re{ n,m } | 重复n到m次 |
a | b |
(re) | 匹配 re,并捕获文本到自动命名的组里 |
(?: re) | 匹配re,不捕获匹配的文本,也不给此分支分配组号 |
?> re | 贪婪子表达式 |
\w | 匹配字母或数字或下划线或汉字 |
\\w | 匹配任意不是字母,数字,下划线,汉字的字符 |
\s | 匹配任意的空白符,相当于[\t\n\r\f] |
\S | 匹配任意不是空白字符的字符 |
\d | 匹配数字,类似[0-9] |
\D | 匹配任意非数字的字符 |
\G | 当前搜索的开头 |
\n | 换行符 |
\b | 通常是单词分界位置,但如果在字符类里使用代表退格 |
\B | 匹配不是单词开头或结束的位置 |
\t | 制表符 |
\Q | 开始引号:\Q(a+b)*3\E 可匹配文本"(a+b)*3" |
\E | 结束引号:\Q(a+b)*3\E 可匹配文本"(a+b)*3" |
正则表达式实例:
实例 | 描述 |
---|---|
. | 匹配除"\r\n"之外的任何单个字符 |
[Rr]uby | 匹配"Ruby"或"ruby" |
rub[ye] | 匹配"ruby"或"rube" |
[aeiou] | 匹配小写字母:aeiou |
[0-9] | 匹配任何数字,类似[0123456789] |
[a-z] | 匹配任何ASCII小写字母 |
[A-Z] | 匹配任何ASCII大写字母 |
[a-zA-Z0-9] | 匹配数字,大写字母 |
[^aeiou] | 匹配除了aeiou以外的其它字符 |
[^0-9] | 匹配除了数字的其它字符 |
\d | 匹配数字,类似:[0-9] |
\D | 匹配非数字,类似:[^0-9] |
\s | 匹配空格,类似:[\t\r\n\f] |
\S | 匹配非空格,类似[^\t\r\n\f] |
\w | 匹配字母,数字,下划线,类似:[A-Za-z0-9_] |
\W | 匹配非字母,数字,下划线:类似:[^A-Za-z0-9] |
ruby? | 匹配"rub"或"ruby":y是可选的 |
ruby* | 匹配"rub"加上0个或多个的y |
ruby+ | 匹配"rub"加上一个或多个的y |
\d{3} | 刚好匹配3个数字 |
\d{3,} | 匹配3个或多个数字 |
\d{3,5} | 匹配3个、4个或5个数字 |
\D\d+ | 无分组:+重复\d |
(\D\d)+/ | 分组:+重复\D\d对 |
([Rr]uby(,)?)+ | 匹配"Ruby"、“Ruby,ruby,ruby”,等等 |
注意上表中的每个字符使用了两个反斜线。这是因为住在Java和Scala中字符串中的反斜线是转义字符。所以如果你要输出\,你需要在字符串中写成.\.来获取一个反斜线。查看以下实例:
import scala.util.matching.Regex
object ChangeLongApp {
def main(args: Array[String]) {
val pattern = new Regex("abl[ae]\\d+")
val str = "ablaw is able1 and cool"
println((pattern findAllIn str).mkString(","))
}
}
输出:able1
三、Scala异常处理
- Scala的异常处理和其它语言比如Java类似。
- Scala的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。
抛出异常:
- Scala抛出异常的方法和Java一样,使用throw方法,例如,抛出一个新的参数异常:
throw new IllegalArgumentException
捕获异常
-
异常捕捉的机制与其他语言中一样,如果有异常发生,catch字句是按次序捕捉的。因此,在catch字句中,越具体的异常越要靠前,越普遍的异常越要靠后。如果抛出的异常不在catch字句中,则异常无法被处理,会被升级到调用者处。
-
捕捉异常的catch子句,语法和其他语言中不太一样。在Scala中,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列的case字句,如下列所示:
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
object Test {
def main(args: Array[String]) {
try {
val f = new FileReader("input.txt")
}catch {
case ex: FileNotFoundException => {
println("Missing file exception")
}
case ex: IOException => {
println("IO Exception")
}
}
}
}
执行以上代码,输出结果为:
- Missing file exception
更换路径:
- val f = new FileReader(“C:/Users/Administrator/Desktop/testInfo1.txt”)
- 输出打印:无异常信息
小结:
- catch 字句里的内容跟match里的case是完全一样的。由于异常捕捉是按次序,如果是最普遍的异常,Throwable,写在最前面,则在它后面的case都捕捉不到,因此需要将它写在最后面。
finally语句
- finally语句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,实例如下:
import java.io.{FileNotFoundException, FileReader, IOException}
object Text {
def main(args: Array[String]) {
try {
val f = new FileReader("input.txt")
} catch {
case ex: FileNotFoundException => {
println("Missing file exception")
}
case ex: IOException => {
println("IOException")
}
}finally {
println("Exiting finally.......")
}
}
}
输出结果:
Missing file exception
Exiting finally.......
2、更换读入文件路径:
val f = new FileReader("C:/Users/Administrator/Desktop/testInfo.txt")
输出结果:
Exiting finally.......
由上面的测试说明:finally语句用于执行不管是正常处理还是有异常发生时都需要执行的步骤。
四、Scala Iterator(迭代器)
Scala Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法。
迭代器it的两个基本操作是next和hasNext。
调用it.next()会返回迭代器的下一个元素,并且更新迭代器的状态。
调用it.hasNext()用于检测集合中是否还有元素。
让迭代器it逐个返回所有元素最简单的方法就是使用while循环。
def main(args: Array[String]) {
val it = Iterator("百度","京东","饿了么","美团","小米","腾讯","阿里")
while (it.hasNext){
println(it.next())
}
}
输出结果:
百度
京东
饿了么
美团
小米
腾讯
阿里
查找最大与最小元素:
- 你可以使用it.min和it.max方法从迭代器中查找最大与最小元素,实例如下:
object IteratorApp {
def main(args: Array[String]): Unit = {
val ita = Iterator(20,40,2,57,98)
val itb = Iterator(20,40,50,2,69,90)
println("最大元素是:" + ita.max)
println("最小元素是:" + itb.min)
}
}
输出结果:
最大元素是:98
最小元素是:2
获取迭代器长度:
object IteratorApp{
def main(args: Array[String]): Unit = {
val ita = Iterator(20,30,2,50,70,90)
val itb = Iterator(25,35,2,40,60)
println("ita.size的值:" + ita.size)
println("itb.size的值:" + itb.size)
}
输出结果:
ita.size的值:6
itb.size的值:5
Scala Iterator常用方法:
方法 | 描述 |
---|---|
def hasNext:Boolean | 如果还有可返回的元素,返回true |
def next():A | 返回迭代器的下一个元素,并且更新迭代器的状态 |
def ++(that: => Iterator[A]):Iterator[A] | 合并两个迭代器 |
def ++[B >:A](that :=> GenTraversableOnce[B]):Iterator[B] | 合并两个迭代器 |
def addString(b: StringBuilder, sep: String): StringBuilder | 添加一个字符串到 StringBuilder b,并指定分隔符 |
https://www.runoob.com/scala/scala-iterators.html
迭代器addString举例:
- 一脸懵逼不知道方法怎么用怎么办呢?
- 直接定位到源码,ita.addString,Ctrl + 鼠标左键,
在源码中有完整的定义和例子说明,应该传入几个参数。
/** Appends all elements of this $coll to a string builder using start, end, and separator strings.
* The written text begins with the string `start` and ends with the string `end`.
* Inside, the string representations (w.r.t. the method `toString`)
* of all elements of this $coll are separated by the string `sep`.
*
* Example:
*
* {{{
* scala> val a = List(1,2,3,4)
* a: List[Int] = List(1, 2, 3, 4)
*
* scala> val b = new StringBuilder()
* b: StringBuilder =
*
* scala> a.addString(b , "List(" , ", " , ")")
* res5: StringBuilder = List(1, 2, 3, 4)
* }}}
*
* @param b the string builder to which elements are appended.
* @param start the starting string.
* @param sep the separator string.
* @param end the ending string.
* @return the string builder `b` to which elements were appended.
*/
def addString(b: StringBuilder, start: String, sep: String, end: String): StringBuilder = {
var first = true
b append start
for (x <- self) {
if (first) {
b append x
first = false
}
else {
b append sep
b append x
}
}
b append end
b
}
五、Scala Collection(集合)
-
Scala提供了一套很好的集合实现,提供了一些集合类型的抽象。
-
Scala集合分为可变和不可变的集合。
-
可变集合可以在适当的地方被更新或扩展。这意味着你可以修改、添加、移除一个集合的元素。
-
而不可变集合类,相比之下,永远不会变。不过,你仍然可以模拟添加、移除、或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
序号 | 集合及描述 |
---|---|
1 | Scala List(列表) List的特征是其元素以现行方式存储,集合中可以存放重复对象,参考API文档 |
2 | Scala Set(集合) Set是最简单的一种集合。集合中的对象不按特定的方式排序,它的每一个元素都包含一对键对象和值对象参考API文档 |
3 | Scala Map(映射) Map是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象,参考API文档 |
4 | Scala元组 元组是不同类型的值的集合 |
5 | Scala Option Option[T] 表示有可能包含值的容器,也可能不包含值 |
6 | Scala Iterator1(迭代器) 迭代器不是一个容器,更确切的说是逐一访问容器内元素的方法 |