大数据实验教学系统
Scala语言基础
变量是一种使用方便的占位符,用于引用计算机内存地址,变量创建后会占用一定的内存空间。
基于变量的数据类型,操作系统会进行内存分配并且决定什么将被储存在保留内存中。因此,通过给变量分配不同的数据类型,你可以在这些变量中存储整数,小数或者字母。
在学习如何声明变量与常量之前,我们先来了解一些变量与常量。
• 一、变量:在程序运行过程中其值可能发生改变的量叫做变量。如:时间,年龄。
• 二、常量 在程序运行过程中其值不会发生变化的量叫做常量。如:数值 3,字符’A’。
Scala IF…ELSE 语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。
可以通过下图来简单了解条件语句的执行过程:
有的时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。
编程语言提供了更为复杂执行路径的多种控制结构。
循环语句允许我们多次执行一个语句或语句组,下面是大多数编程语言中循环语句的流程图:
Scala提供了一套很好的集合实现,提供了一些集合类型的抽象。
Scala 集合分为可变的和不可变的集合。
可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
而不可变集合类,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
Scala 的异常处理和其它语言比如 Java 类似。
Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。
掌握Scala基本语法(变量、表达式、流程控制)
掌握Scala函数定义和参数传递(位置参数、默认参数、名称参数等)
掌握Scala常用集合数据结构(数组、列表、元组、Map等)
掌握Scala异常处理
1、学会使用Scala基本语法,包含变量、表达式、流程控制等。
2、学会使用Scala定义函数定义和传递参数,包括位置参数、默认参数、名称参数等。
3、学会使用Scala常用集合数据结构,包括数组、列表、元组、Map等。
4、学会使用Scala进行异常处理。
硬件:x86_64 ubuntu 16.04服务器
软件:JDK 1.8,Scala-2.11.x
在终端中,执行以下命令,启动scala shell:
1. $ scala
1、变量。
在 Scala 中,使用关键词 “var” 声明变量,使用关键词 “val” 声明常量。
在paste模式下,声明变量并修改变量:
1. var myVar : String = "Foo"
2. myVar = "New Foo"
按下Ctrl+D,执行以上代码。输出结果如下:
myVar: String = New Foo
myVar: String = New Foo
在paste模式下,声明常量并修改常量:
1. val myVal : String = "Smart"
2. myVal = "New Smart"
按下Ctrl+D,执行以上代码。输出结果如下:
:12: error: reassignment to val
myVal = “New Smart”
^
在上面的代码中定义了常量 myVal,它是不能修改的。如果程序尝试修改常量 myVal 的值,程序将会在编译时报错。
使用lazy关键字可实现惰性求值特性,这允许用户延迟任何表达式的执行。当使用lazy关键字声明表达式时,它只会在显式调用时执行:
1. val x = 3
2. val y = 5
3. lazy val sum = y - x
4. println(sum)
按下Ctrl+D,执行以上代码。输出结果如下:
2
x: Int = 3
y: Int = 5
sum: Int = <lazy>
对于lazy变量,只有在调用它时才计算它。需要注意的是,惰性计算特性只能在val中使用。
2、变量数据类型推断
在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。
所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。
1. var myVar = 10;
2. val myVal = "Hello, Scala!";
按下Ctrl+D,执行以上代码。输出结果如下:
myVar: Int = 10
myVar: String = Hello, Scala!
以上代码中,变量myVar会被推断为 Int 类型,myVal 会被推断为 String 类型。
3、Scala 多个变量声明。
Scala 支持多个变量的声明:
1. val xmax, ymax = 100 // 变量xmax, ymax都赋值为100
按下Ctrl+D,执行以上代码。输出结果如下:
xmax: Int = 100
ymax: Int = 100
4、Scala表达式。
在Scala中, 一切皆可视为表达式:
Scala 中还可以使用多行表达式,使用花括号{} 包含。 多行表达式的最后一行语句执行的结果即为整个多行表达式的结果。例如,在paste模式下,键入以下代码:
1. var diff_salary = {
2. var salary_PHP = 15000
3. var salary_SCALA = 25000
4. salary_SCALA - salary_PHP
5. }
按下Ctrl+D,执行以上代码。输出结果如下:
diff_salary: Int = 10000
5、Scala流程控制。
• if语句。
if语句语句由布尔表达式及之后的语句块组成。
如果布尔表达式为 true 则执行大括号内的语句块,否则跳过大括号内的语句块,执行大括号之后的语句块。
在paste模式下,键入以下内容:
1. var x = 10;
2.
3. if( x < 20 ){
4. println("x < 20");
5. }
按下Ctrl+D,执行以上代码。输出结果如下:
x < 20
x: Int = 10
• if…else 语句。
if 语句后可以紧跟 else 语句,else 内的语句块可以在布尔表达式为 false 的时候执行。
在paste模式下,键入以下内容:
1. var x = 30;
2.
3. if( x < 20 ){
4. println("x 小于 20");
5. }else{
6. println("x 大于 20");
7. }
按下Ctrl+D,执行以上代码。输出结果如下:
x 大于 20
x: Int = 30
• if…else if…else 语句。
if 语句后可以紧跟 else if…else 语句,在多个条件判断语句的情况下很有用。
在paste模式下,键入以下内容:
1. var x = 30;
2.
3. if( x == 10 ){
4. println("X 的值为 10");
5. }else if( x == 20 ){
6. println("X 的值为 20");
7. }else if( x == 30 ){
8. println("X 的值为 30");
9. }else{
10. println("无法判断 X 的值");
11. }
按下Ctrl+D,执行以上代码。输出结果如下:
X 的值为 30
x: Int = 30
• if…else 嵌套语句。
if…else 嵌套语句可以实现在 if 语句内嵌入一个或多个 if 语句。
在paste模式下,键入以下内容:
1. var x = 30;
2. var y = 10;
3.
4. if( x == 30 ){
5. if( y == 10 ){
6. println("X = 30 , Y = 10");
7. }
8. }
按下Ctrl+D,执行以上代码。输出结果如下:
X = 30 , Y = 10
x: Int = 30
y: Int = 10
while 循环
Scala 的 while 循环和其它语言如 Java 功能一样,它含有一个条件,和一个循环体,只有条件满足,就一直执行循环体的代码。
在paste模式下,键入以下内容:
1. var a=0
2. while( a<5) {
3. println(a)
4. a = a + 1
5. }
按下Ctrl+D,执行以上代码。输出结果如下:
0
1
2
3
4
a: Int = 5
• do…while 循环。
Scala 也有 do-while 循环,它和 while 循环类似,只是检查条件是否满足在循环体执行之后检查。
在paste模式下,键入以下内容:
1. var a=0
2. do{
3. a = a + 1
4. println(a)
5. } while( a<5)
按下Ctrl+D,执行以上代码。输出结果如下:
1
2
3
4
5
a: Int = 5
• for 循环
Scala 中的 for 表达式有如一把完成迭代任务的瑞士军刀,它允许你使用一些简单的部件以不同的方法组合可以完成许多复杂的迭代任务。简单的应用比如枚举一个整数列表,较复杂的应用可以同时枚举多个不同类型的列表,根据条件过滤元素,并可以生成新的集合。
在paste模式下,键入以下内容:
1. for (i <- 1 to 5) println(i)
在上面语句中,i不需要提前进行变量声明,可以在for语句括号中的表达式中直接使用。语句中,“<-”表示,之前的i要遍历后面1到5的所有值。结果:
1
2
3
4
5
• 注意to和until的区别:
在paste模式下,键入以下内容:
1. for( a <- 1 until 5){
2. println(a);
3. }
按下Ctrl+D,执行以上代码。输出结果如下:
1
2
3
4
在for循环中可以使用分号 (;)
来设置多个区间,它将迭代给定区间所有的可能值。
在paste模式下,键入以下内容:
1. for( a <- 1 to 3; b <- 1 to 3){
2. print( "a = " + a )
3. println( ", b = " + b )
4. }
按下Ctrl+D,执行以上代码。输出结果如下:
a = 1, b = 1
a = 1, b = 2
a = 1, b = 3
a = 2, b = 1
a = 2, b = 2
a = 2, b = 3
a = 3, b = 1
a = 3, b = 2
a = 3, b = 3
• Scala并没有提供break或continue语句来退出循环。那么如果需要break时我们该怎么做呢?
在paste模式下,键入以下内容:
1. import scala.util.control.Breaks._
2.
3. var n = 15
4. breakable {
5. for(c <- "12345678910") {
6. if(n == 10) {
7. println()
8. break
9. } else {
10. print(c)
11. }
12. n -= 1
13. }
14. }
按下Ctrl+D,执行以上代码。输出结果如下:
12345
import scala.util.control.Breaks._
n: Int = 10
6. 简单模式匹配。
一个模式匹配的简单应用是作为多级if-else 语句的替代,这可以提高代码的可读性。模式匹配不使用关键字switch,Scala 使用关键字match。每个可能的匹配由关键字case 处理。如果有一个case 被匹配到,那么右箭头右侧的代码被执行。其中下划线_代表默认case。如果前面没有一个case匹配上的话,默认case 的代码会被执行。与switch 语句不同,在每个case 后的代码不需要break 语句。只有匹配的case 会被执行。另外,每个=>右侧的代码是一个表达式,返回一个值。因此,一个模式匹配语句本身是一个表达式,返回一个值。
在paste模式下,键入以下内容:
1. val math = 3
2. val simple = math match{
3. case 1 => "西普"
4. case 2 => "其他"
5. case _ => "不正确"
6. }
7. println(simple)
按下Ctrl+D,执行以上代码。输出结果如下:
不正确
math: Int = 3
simple: String = 不正确
1、默认参数。
Scala 可以为函数参数指定默认参数值。如果在调用函数的过程中可以不传递参数,这时函数就会调用它的默认参数值;如果传递了参数,则传递值会取代默认值。
在paste模式下,键入以下内容:
1. def addInt( a:Int=5, b:Int=7 ) : Int = {
2. var sum:Int = 0
3. sum = a + b
4. sum
5. }
6. println( "返回值 : " + addInt() );
按下Ctrl+D,执行以上代码。输出结果如下:
返回值 : 12
2、命名参数。
通常情况下,调用函数时,参数传入和函数定义时参数列表一一对应。
在paste模式下,键入以下内容:
1. def speed(distance: Float, time:Float) :Float = distance/time
2. speed(100,10)
按下Ctrl+D,执行以上代码。输出结果如下:
Float = 10.0
而使用命名参数则允许我们使用任意顺序传入参数。在paste模式下,键入以下内容:
1. speed(time=10,distance=100)
2. speed(distance=100,time=10)
按下Ctrl+D,执行以上代码。输出结果如下:
Float = 10.0
3、变长参数。
Scala在定义函数时允许指定最后一个参数可以重复(变长参数),从而允许函数调用者使用变长参数列表来调用该函数,Scala中使用”*”来指明该参数为重复参数。
在paste模式下,键入以下内容:
1. def echo (args: String *) = for (arg <- args) println(arg)
2. echo ("One")
3. echo ("Hello","World")
按下Ctrl+D,执行以上代码。输出结果如下:
One
Hello
World
1、数组。
定长数组,就是长度不变的数组,在Scala中使用Array进行声明。
在paste模式下,键入以下内容:
1. var arr = Array(1, 3, 2, 4)
2.
3. //求和
4. println(arr.sum)
5.
6. //最大值
7. println(arr.max)
8.
9. //最小值
10. println(arr.min)
11.
12. //转化成字符串
13. println(arr.mkString("|"))
14.
15. //按大小排序
16. println(arr.sorted.toBuffer)
17.
18. //数组倒序
19. println(arr.reverse.toBuffer)
20.
21. // 将数组(元素是元组类型)转换为Map
22. println(Array(("cn", "china"), ("fr", "french")).toMap)
按下Ctrl+D,执行以上代码。输出结果如下:
10
4
1
1|3|2|4
ArrayBuffer(1, 2, 3, 4)
ArrayBuffer(4, 2, 3, 1)
Map(cn -> china, fr -> french)
不定长数组,就是长度可变的数组,在Scala中使用ArrayBuffer进行声明。
在paste模式下,键入以下内容:
1. import scala.collection.mutable.ArrayBuffer
2.
3. var arrbuffer = ArrayBuffer(1, 5, 3, 7)
4.
5. //求和
6. println(arrbuffer.sum)
7.
8. //最大值
9. println(arrbuffer.max)
10.
11. //最小值
12. println(arrbuffer.min)
13.
14. //转化成字符串
15. println(arrbuffer.mkString("|"))
16.
17. //按大小排序(由小到大)
18. println(arrbuffer.sorted.toBuffer)
19.
20. //按大小排序(由大到小)
21. println(arrbuffer.sorted.reverse.toBuffer)
22.
23. //数组倒序
24. println(arrbuffer.reverse.toBuffer)
25.
26. // 将数组(元素是元组类型)转换为Map
27. println(Array(("cn", "china"), ("fr", "french")).toMap)
28.
29. // 增加一个元素
30. arrbuffer += 20
31. println(arrbuffer)
32.
33. // 增加一个数组集合
34. arrbuffer ++= Array(50, 60)
35. println(arrbuffer)
36.
37. // 删除最后 3 个元素
38. arrbuffer.trimEnd(3)
39. println(arrbuffer)
40.
41. // 在索引2处插入两个元素
42. arrbuffer.insert(2, 28, 29)
43. println(arrbuffer)
44.
45. // 在索引2处删除三个元素
46. arrbuffer.remove(2, 3)
47. println(arrbuffer)
48.
49. // 清空数组
50. arrbuffer.clear()
51. println(arrbuffer)
按下Ctrl+D,执行以上代码。输出结果如下:
16
7
1
1|5|3|7
ArrayBuffer(1, 3, 5, 7)
ArrayBuffer(7, 3, 5, 1)
Map(cn -> china, fr -> french)
ArrayBuffer(1, 5, 3, 7, 20)
ArrayBuffer(1, 5, 3, 7, 20, 50, 60)
ArrayBuffer(1, 5, 3, 7)
ArrayBuffer(1, 5, 28, 29, 3, 7)
ArrayBuffer(1, 5, 7)
ArrayBuffer()
数组遍历。
在paste模式下,键入以下内容:
1. val intValueArr2 = Array(0,1,2);
2. //遍历
3. for(i <- intValueArr2) println(i);
;按下Ctrl+D,执行以上代码。输出结果如下:
0
1
2
2、列表。
在Scala中,列表分为不可变的和可变的。不可变列表的实现类为List,可变数组的实现类为ListBuffer。
在paste模式下,键入以下代码:
1. val intList = List(1,2,3)
2. val intListOther = 0::intList
按下Ctrl+D,执行以上代码。输出结果如下:
intList: List[Int] = List(1, 2, 3)
intListOther: List[Int] = List(0, 1, 2, 3)
注意,上面操作执行后,intList不会发生变化,依然是List(1,2,3),intListOther是一个新的列表List(0,1,2,3)
列表有头部和尾部的概念,可以使用intList.head来获取上面定义的列表的头部,值是1,使用intList.tail来获取上面定义的列表的尾部,值是List(2,3),可以看出,头部是一个元素,而尾部则仍然是一个列表。
由于列表的头部是一个元素,所以,我们可以使用::操作,在列表的头部增加新的元素,得到一个新的列表。::操作符是右结合的,因此,如果要构建一个列表List(1,2,3),实际上也可以采用下面的方式:
1. val intList = 3::2::1::Nil
执行结果如下:
intList: List[Int] = List(3, 2, 1)
上面代码中,Nil表示空列表。
我们也可以使用:::操作符对不同的列表进行连接得到新的列表。例如,在paste模式下,键入以下内容:
1. val intList1 = List(1,2)
2. val intList2 = List(3,4)
3. val intList3 = intList1:::intList2
按下Ctrl+D,执行以上代码。输出结果如下:
intList1: List[Int] = List(1, 2)
intList2: List[Int] = List(3, 4)
intList3: List[Int] = List(1, 2, 3, 4)
注意,执行上面操作后,intList1和intList2依然存在,intList3是一个全新的列表。
实际上,Scala还为列表提供了一些常用的方法,比如,如果要实现求和,可以直接调用sum方法,如下:
1. intList.sum
如果要遍历列表,可以使用for循环。例如,在paste模式下,键入以下代码:
1. val intList = List(1,2,3)
2.
3. //遍历
4. for(i <- intList) println(i);
按下Ctrl+D,执行以上代码。输出结果如下:
1
2
3
3、元组。
元组是不同类型的值的聚集。元组和列表不同,列表中各个元素必须是相同类型,而元组可以包含不同类型的元素。
下面我们声明一个名称为tuple的元组。在paste模式下,键入以下代码:
1. val tuple = ("西普",666,666,666);
2.
3. println("我是"+tuple._1);
4. println("我还是"+tuple._2);
5. println(tuple._1+tuple._3);
按下Ctrl+D,执行以上代码。输出结果如下:
我是西普
我还是666
西普666
从上述代码的执行效果可以看出,我们声明一个元组是很简单的,只需要用圆括号把多个元组的元素包围起来就可以了。
当需要访问元组中的某个元素的值时,可以通过类似tuple._1、tuple._2、tuple._3这种方式就可以实现。
4、映射。
在Scala中,映射(Map)是一系列键值对的集合,也就是,建立了键和值之间的对应关系。在映射中,所有的值,都可以通过键来获取。
映射包括可变和不可变两种,默认情况下创建的是不可变映射,如果需要创建可变映射,需要引入scala.collection.mutable.Map包。
下面我们创建一个不可变映射:
1. val university = Map("xipu" -> "xi pu jiao yu", "THU" -> "Tsinghua University","PKU"->"Peking University")
执行以上代码。输出结果如下:
university: scala.collection.immutable.Map[String,String] = Map(XMU -> Xiamen University, THU -> Tsinghua University, PKU -> Peking University)
如果要获取映射中的值,可以通过键来获取。代码如下:
1. println(university("xipu"))
执行以上代码。输出结果如下:
xi pu jiao yu
上面代码通过”xipu”这个键,可以获得值”xi pu jiao yu”。
如果要检查映射中是否包含某个值,可以使用contains方法。例如,在paste模式下,键入以下代码:
1. val xmu = if (university.contains("xipu")) university("xipu") else 0
2. println(xmu)
按下Ctrl+D,执行以上代码。输出结果如下:
xi pu jiao yu
xmu: Any = xi pu jiao yu
上面我们定义的是不可变映射,是无法更新映射中的元素的,也无法增加新的元素。如果要更新映射的元素,就需要定义一个可变的映射。
在paste模式下,键入以下代码:
1. import scala.collection.mutable.Map
2. val university2 = Map("XMU" -> "xi pu jiao yu", "THU" -> "Tsinghua University","PKU"->"Peking University")
3. university2("XMU") = "西普教育" //更新已有元素的值
4. university2("ZZU") = "Zhengzhou University" //添加新元素
按下Ctrl+D,执行以上代码。输出结果如下:
import scala.collection.mutable.Map
university2: scala.collection.mutable.Map[String,String] = Map(XMU -> xi pu jiao yu, THU -> Tsinghua University, ZZU -> Zhengzhou University, PKU -> Peking University)
如果要遍历映射,可以使用for循环语句。基本格式是:for ((k,v) <- 映射) 语句块
在paste模式下,键入以下代码:
1. val university = Map("XMU" -> "xi pu jiao yu", "THU" -> "Tsinghua University","PKU"->"Peking University")
2. for ((k,v) <- university) printf("代码: %s,名称: %s
3. ",k,v)
按下Ctrl+D,执行以上代码。输出结果如下:
代码: XMU,名称: xi pu jiao yu
代码: THU,名称: Tsinghua University
代码: PKU,名称: Peking University
或者,也可以只遍历映射中的k或者v。比如说,我们只想把所有键打印出来:
1. for (k<-university.keys) println(k)
输出结果如下:
XMU
THU
PKU
再比如说,我们只想把所有值打印出来:
1. for (v<-university.values) println(v)
输出结果如下:
xi pu jiao yu
Tsinghua University
Peking University
1、抛出异常。
Scala 抛出异常的方法和 Java一样,使用 throw 方法,例如,抛出一个新的参数异常:
1. throw new IllegalArgumentException
2、捕获异常。
异常捕捉的机制与其他语言中一样,如果有异常发生,catch字句是按次序捕捉的。因此,在catch字句中,越具体的异常越要靠前,越普遍的异常越靠后。 如果抛出的异常不在catch字句中,该异常则无法处理,会被升级到调用者处。
捕捉异常的catch子句,语法与其他语言中不太一样。在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case字句。
在paste模式下,键入以下代码:
1. import java.io.FileReader
2. import java.io.FileNotFoundException
3. import java.io.IOException
4.
5. try {
6. val f = new FileReader("input.txt")
7. } catch {
8. case ex: FileNotFoundException =>{
9. println("Missing file exception")
10. }
11. case ex: IOException => {
12. println("IO Exception")
13. }
14. }
按下Ctrl+D,执行以上代码。输出结果如下:
Missing file exception
catch字句里的内容跟match里的case是完全一样的。由于异常捕捉是按次序,如果最普遍的异常,Throwable,写在最前面,则在它后面的case都捕捉不到,因此需要将它写在最后面。
3、finally 语句。
finally 语句用于执行不管是正常处理还是有异常发生时都需要执行的步骤。
在paste模式下,键入以下代码:
1. import java.io.FileReader
2. import java.io.FileNotFoundException
3. import java.io.IOException
4.
5. try {
6. val f = new FileReader("input.txt")
7. } catch {
8. case ex: FileNotFoundException =>{
9. println("Missing file exception")
10. }
11. case ex: IOException => {
12. println("IO Exception")
13. }
14. }finally {
15. println("Exiting finally...")
16. }
按下Ctrl+D,执行以上代码。输出结果如下:
Missing file exception
Exiting finally…
实验结果运行准确,无误
经过本节实验的学习,通过使用Shell的方式,学习了使用Scala基本语法,包含变量、表达式、流程控制,使用Scala定义函数定义和传递参数,包括位置参数、默认参数、名称参数,使用Scala常用集合数据结构,包括数组、列表、元组、Map,使用Scala进行异常处理,进一步巩固了我们的scala基础。
关于Scala中函数的定义和使用,总结如下:
scala使用def关键字定义函数。
return x + y;