大数据开发工程师-第九周 第2章 Scala基础语法


第九周 第2章 Scala基础语法

Scala的基本使用

变量

1
2
3
scala中的变量分为两种:可变var和不可变val
可变var:可以随时修改var声明的变量的值
不可变val:val声明的变量,值不能被修改,否则会报错: error: reassignment to val
1
2
3
4
5
6
7
8
9
scala> var a = 1
a: Int = 1
scala> a = 2
a: Int = 2
scala> val b = 1
b: Int = 1
scala> b = 2
<console>:8: error: reassignment to val
b = 2
1
2
3
4
5
6
7
8
9
10
注意:在实际工作中,针对一些不需要改变值的变量,通常建议使用val,这样可以不用担心值被错误的修改(等于java中的final类型)。这样可以提高系统的稳定性和健壮性!

无论声明val变量,还是声明var变量,都可以手动指定变量的类型
如果不指定,Scala会自动根据值,进行类型推断
val c = 1 等价于 val c: Int = 1

scala> val c = 1
c: Int = 1
scala> val c: Int = 1
c: Int = 1

数据类型

1
2
3
4
5
6
Scala中的数据类型可以分为两种,基本数据类型和增强版数据类型
基本数据类型有:Char、Byte、Short、Int、Long、Float、Double、Boolean
增强版数据类型有: StringOps、RichInt、RichDouble、RichChar 等

scala使用这些增强版数据类给基本数据类型增加了上百种增强的功能
例如:RichInt提供的有一个to函数, 1.to(10) ,此处Int会先隐式转换为RichInt,然后再调用其to函数
1
2
3
4
5
6
scala> 1.to(10)
res2: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7,

注意,to函数还可以这样写
scala> 1 to 10
res3: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7,
1
2
3
使用基本数据类型,直接就可以调用RichInt中对应的函数
scala> 1.toString() //空号也可以不用
res4: String = 1

操作符

1
2
3
4
Scala的算术操作符与Java的算术操作符没有什么区别
比如 +、-、*、/、% 等,以及 &、|、^、>>、<< 等
注意:Scala中没有提供++、--操作符
我们只能使用+和-,比如count = 1,count++是错误的,必须写做count += 1

if 表达式

1
2
3
4
5
6
在Scala中,if表达式是有返回值的,就是if或者else中最后一行语句返回的值,这一点和java中的if是不一样的,java中的if表达式是没有返回值的

scala> val age = 20
age: Int = 20
scala> if(age > 18) 1 else 0
res9: Int = 1
1
2
3
4
在这因为if表达式是有返回值的,所以可以将if表达式赋予一个变量

scala> val res = if(age > 18) 1 else 0
res: Int = 1
1
2
3
4
5
6
由于if表达式是有值的,而if和else子句的值的类型可能还不一样,此时if表达式的值是什么类型呢?

注意:Scala会自动进行推断,取两个类型的公共父类型

例如,if(age > 18) 1 else 0,表达式的类型是Int,因为1和0都是Int
例如,if(age > 18) “old” else 0,此时if和else的值分别是String和Int,则表达式的值是Any类型,Any是String和Int的公共父类型
1
2
3
4
scala> if(age > 18) 1 else 0
res12: Int = 1
scala> if(age > 18) "old" else 0
res13: Any = old
1
2
3
4
5
6
如果if后面没有跟else,则默认else的值是Unit,也可以用()表示,类似于java中的void或者null
例如,val age = 12; if(age > 18) “old”。此时就相当于if(age > 18) “old” else ()。
此时表达式的值是Any

scala> if(age > 18) "old" else ()
res17: Any = ()
1
2
3
4
如果想在scala REPL中执行多行代码,该如何操作?
使用 :paste 和 ctrl+D 的方式
:paste 表示代码块的开始
ctrl+D 表示代码块的结束
1
2
3
4
5
6
7
8
9
10
scala> :paste
// Entering paste mode (ctrl-D to finish)
val age = 20
if(age > 18)
1
else
0
// Exiting paste mode, now interpreting.
age: Int = 20
res18: Int = 1

语句终结符

1
2
3
4
5
6
7
Scala默认不需要语句终结符,它将每一行作为一个语句
如果一行要放多条语句,则前面的语句必须使用语句终结符
语句终结符和Java中的一样,就是我们平时使用的分号

scala> val age = 20; if(age > 18) 1 else 0
age: Int = 20
res0: Int = 1

循环

print和println

1
2
在讲循环之前,先来看一下打印命令print和println
print打印时不会加换行符,而println打印时会加一个换行符,这个特性和Java中的打印语句的特性是一样的

for循环

1
for循环本身的特性就没什么好说的了,直接上案例,主要注意一下scala中的for和java中的for在语法层面的区别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
scala> :paste
// Entering paste mode (ctrl-D to finish)
val n = 10
for(i <- 1 to n) //1.to(n) 一样的
println(i)
// Exiting paste mode, now interpreting.
1
2
3
4
5
6
7
8
9
10
n: Int = 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这里面的to可以换成until

scala> :paste
// Entering paste mode (ctrl-D to finish)
val n = 10
for(i <- 1 until 10)
println(i)
// Exiting paste mode, now interpreting.
1
2
3
4
5
6
7
8
9
n: Int = 10
1
2
3
4
5
6
对比两次执行的结果发现
1 to 10 可以获取1~10之间的所有数字
1 until 10可以获取1~9之间的所有数字

所以在这需要注意了,to 和 until 其实都是函数,一个是闭区间,一个是开区间
具体用哪个就要看你的需求了
1
2
3
4
5
6
7
8
9
10
11
12
for循环针对字符串还可以用
scala> for(c <- "hello scala") println(c)
h
e
l
l
o
s
c
a
l
a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
注意:在这里我在for循环后面没有使用花括号,都省略了,主要是因为for循环的循环体代码就只有一行,如果有多行,就需要使用花括号了,否则,最终执行的结果就不是我们想要的

scala> :paste
// Entering paste mode (ctrl-D to finish)
for(i <- 1 to 5)
println(i)
println("hehe")
// Exiting paste mode, now interpreting.
1
2
3
4
5
hehe

while循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
while循环,它的用法和java中的while也是很像的,主要看一下语法层面的区别

scala> :paste
// Entering paste mode (ctrl-D to finish)
var n = 10
while(n>0){
println(n)
n -= 1
}
// Exiting paste mode, now interpreting.
10
9
8
7
6
5
4
3
2
1
n: Int = 0

高级for循环

if守卫模式
1
2
3
4
最后来看一下高级for循环的用法
if守卫
if守卫模式,假设我们想要获取1~10之间的所有偶数,使用普通的for循环,需要把每一个数字都循环出来,然后判断是否是偶数
如果在for循环里面使用if守卫,可以在循环的时候就执行一定的逻辑,判断数值是否是偶数
1
2
3
4
5
6
scala> for(i <- 1 to 10 if i % 2 == 0) println(i)
2
4
6
8
10
for推导式
1
2
3
4
5
6
for推导式,一个典型例子是构造集合

我们在使用for循环迭代数字的时候,可以使用yield指定一个规则,对迭代出来的数字进行处理,并且创建一个新的集合

scala> for(i <- 1 to 10) yield i *2
res16: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12

Scala的集合体系

1
接下来整体学习一下Scala中的集合体系,集合在工作中属于经常使用的数据结构

集合体系

1
首先看一下整个集合体系结构,这个结构与Java的集合体系非常相似

bKJxIg.md.png

1
2
3
4
5
6
7
8
集合的顶层接口是Iterable,Iterable接口下面还有一些子接口, Set、Seq、Map
这几个子接口下面有具体的实现类
set下面有HashSet、LinkedHashSet、SortedSet等等
seq下面有List、Buffer、Range等等
Map下面有HashMap、SortedMap、LinkedHashMap等等
其中Buffer下面还有两个常用的,ArrayBuffer、ListBuffer
这是集合中一些常见的实现类
在讲这个集合体系的时候,还会关联讲到 Array和Tuple 这两个数据结构

集合

1
2
3
4
5
6
Scala中的集合是分成可变和不可变两类集合的
其中可变集合就是说,集合的元素可以动态修改
而不可变集合就是说,集合的元素在初始化之后,就无法修改了
可变集合:在 scala.collection.mutable 这个包下面
不可变集合:在 scala.collection.immutable 这个包下面
我们在创建集合的时候,如果不指定具体的包名,默认会使用不可变集合
1
2
3
4
5
6
7
先来看一下Set,Set代表一个没有重复元素的集合
这个集合的特性和Java中Set集合的特性基本一样
Set集合分为可变的和不可变的集合,默认情况下使用的是不可变集合
Set可以直接使用,并且不需要使用new关键字,来看一下

scala> val s = Set(1,2,3)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
Set
不可变set
1
2
这是不是很奇怪,本来Set是一个接口,但是却可以创建对象,更神奇的是竟然还不需要使用new关键字,这就有点颠覆我们的认知了
注意了,大家在学习Scala的时候,可以拿Java来进行对比,加深理解,但是不要全部拿Java里面的知识点来硬套,因为它们两个有些地方还是不一样的。
1
2
3
4
来看一下Scala的文档,你会发现这个Set不仅仅是一个接口,它还是一个Object,具体这个Object类型我
们在后面会详细分析,在这大家先知道这个东西就行了。

注意:本来是应该看对应版本2.12.11的文档的,但是2.12.11文档的格式看起来不是很清晰,所以在这我们就是要2.11.12这个版本了,主要的是没有什么变化的,不影响我们使用
1
2
在这大家可以这样理解,只要前面带有object的,可以直接创建对象,并且不需要使用new关键字
所以set可以直接使用

image-20230322125537843

image-20230322125555757

1
2
3
4
5
6
7
注意:默认情况下直接创建的set集合是一个不可变集合,在这可以看到是在immutable包里面的,不可变集合中的元素一经初始化,就不能改变了,所以初始化后再向里面添加元素就报错了

scala> val s = Set(1,2,3)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> s += 4
<console>:9: error: value += is not a member of scala.collection.immutable.Set
s += 4
1
2
3
4
5
但是注意,我使用s + 4 这种操作是可以的
scala> val s = Set(1,2,3)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> s + 4
res33: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)
1
2
3
这样是不是和我们刚才说的自相矛盾?
不是的,因为 s + 4 返回的是一个新的集合了,相当于在之前的集合的基础上,创建一个新的集合,新的集合包含之前集合的元素和我们新增的4这个元素
这个大家需要能够区分开
可变set
1
2
3
4
5
6
7
如果想要创建一个可变的set集合,可以使用mutable包下面的set集合,显式指定包名

scala> val s = scala.collection.mutable.Set(1,2,3)
s: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
scala> s += 4
res34: s.type = Set(1, 2, 3, 4)
 12345
子类HashSet

image-20230322130004751

1
2
3
4
HashSet:这个集合的特点是:集合中的元素不重复、无序

HashSet集合分为可变和不可变之分, immutable 包下面的是不可变的,后期无法新增元素
在这里可以使用new关键字,也可以不使用,因为HashSet既是class,又是object,但是包名需要指定,否则无法识别
1
2
3
4
5
6
7
8
9
10
scala> val s = new scala.collection.mutable.HashSet[Int]()
s: scala.collection.mutable.HashSet[Int] = Set()
scala> s +=1
res35: s.type = Set(1)
scala> s +=2
res36: s.type = Set(1, 2)
scala> s +=5 //+=是一个方法,s.+=(5)一样
res38: s.type = Set(1, 5, 2)

如果在创建集合的时候就初始化了元素,则可以省略泛型的定义,集合会自动识别元素的类型
子类LinkedHashSet
image-20230322130032040
1
LinkedHashSet:这个集合的特点是:集合中的元素不重复、有序,它会用一个链表维护插入顺序,可以保证集合中元素是有序的
1
2
3
4
5
6
7
8
9
10
LinkedHashSet只有可变的,没有不可变的

scala> val s = new scala.collection.mutable.LinkedHashSet[Int]()
s: scala.collection.mutable.LinkedHashSet[Int] = Set()
scala> s +=1
res42: s.type = Set(1)
scala> s +=2
res43: s.type = Set(1, 2)
scala> s +=5
res44: s.type = Set(1, 2, 5)
子类SortedSet
1
2
3
SortedSet:这个集合的特点是:集合中的元素不重复、有序,它会自动根据元素来进行排序

SortedSet分为可变集合和不可变集合

bKDyTI.md.png

1
2
3
4
5
6
7
8
9
10
下面的那两个SortedSet是上面collection.SortedSet接口的子接口,一般会使用下面那两个。

scala> val s = scala.collection.mutable.SortedSet[String]()
s: scala.collection.mutable.SortedSet[String] = TreeSet()
scala> s +=("c")
res45: s.type = TreeSet(c)
scala> s +=("a")
res46: s.type = TreeSet(a, c)
scala> s +=("b")
res47: s.type = TreeSet(a, b, c)
1
2
3
4
5
6
从这可以看出来SortedSet集合中的元素是按照元素的字典顺序排序的
针对里面这些Set集合,如果想要迭代他们里面的元素,可以使用for循环直接迭代
以SortedSet为例,其它的 Set、HashSet、LinkedHashSet 都是一样的

scala> for(i <- s ) println(i)
a b c
1
如果还有其它需求的话可以到这里来看一下文档

image-20230322130537417

Sequence
List

image-20230322130705196

1
2
接下来看一下List,List属于Seq接口的子接口
List代表一个不可变的列表
1
2
3
创建一个list
scala> val l = List(1, 2, 3, 4)
l: List[Int] = List(1, 2, 3, 4)
1
2
注意:为什么有的地方需要写类的全路径,而有的不需要呢?
由于immutable包是默认导入的,所以不需要导包,但是也会有个别虽然在immutable包下面的,但是不写全路径还是报错,原谅它把,反正你都带全路径肯定是没有问题的,后期我们会使用idea来开发,也不需要考虑包名的问题,不过在这为了演示起来更加清晰,就使用scala的命令行了
head和tail
1
2
3
4
5
6
7
针对List有 head 、 tail 以及 :: 这几个操作
先演示一下 head、tail 操作

scala> l.head
res49: Int = 1
scala> l.tail
res51: List[Int] = List(2, 3, 4)
1
2
3
head:表示获取List中的第一个元素
tail:表示获取List中第一个元素之后的所有元素
那其实head和tail就可以获取list中的所有元素了
: :
1
2
3
4
通过 :: 操作符,可以将head和tail的结果合并成一个List

scala> l.head :: l.tail
res52: List[Int] = List(1, 2, 3, 4)
1
2
3
:: 这种操作符要清楚,在spark源码中是有体现的,一定要能够看懂

针对List中的元素进行迭代和前面讲的Set集合的迭代是一样的
ListBuffer
1
2
3
在这里List是不可变的列表,在实际工作中使用的时候会很不方便,因为我们很多场景下都是需要向列表中动态添加元素,这个时候该怎么办呢?
Scala还提供的有一个ListBuffer
ListBuffer:可以支持动态增加或者移除元素
1
2
3
4
5
6
7
8
9
10
11
12
scala> val lb = scala.collection.mutable.ListBuffer[Int]()
lb: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
scala> lb +=1
res56: lb.type = ListBuffer(1)
scala> lb +=2
res57: lb.type = ListBuffer(1, 2)
scala> lb +=5
res58: lb.type = ListBuffer(1, 2, 5)
scala> lb -=5
res59: lb.type = ListBuffer(1, 2)

ListBuffer也可以直接使用for循环迭代
Map
1
2
Map是一种可迭代的键值对(key/value)结构
Map分为可变和不可变,默认情况下使用的是不可变Map
不可变Map
1
2
3
4
5
创建一个不可变的Map
scala> val ages = Map("jack"->30,"tom"->25,"jessic"->23)
ages: scala.collection.immutable.Map[String,Int] = Map(jack -> 30, tom -> 25,
scala> ages("jack")
res100: Int = 30
可变Map
1
2
3
4
5
6
7
8
9
创建一个可变的Map
scala> val ages = scala.collection.mutable.Map("jack"->30,"tom"->25,"jessic"-
ages: scala.collection.mutable.Map[String,Int] = Map(jessic -> 23, jack -> 30
scala> ages("jack")
res101: Int = 30

还有一种创建Map的简易方式,这种方式创建的是不可变Map
scala> val ages = Map(("jack",30),("tom",25),("jessic"->23))
ages: scala.collection.immutable.Map[String,Int] = Map(jack -> 30, tom -> 25,
查询操作
1
2
3
4
5
6
7
8
1.获取指定key对应的value,如果key不存在,会报错

scala> val ages = scala.collection.mutable.Map(("jack",30),("tom",25),("jessi
ages: scala.collection.mutable.Map[String,Int] = Map(jessic -> 23, jack -> 30
scala> val age = ages("jack")
age: Int = 30
scala> val age = ages("jack1")
java.util.NoSuchElementException: key not found: jack
1
2
3
4
5
6
7
8
所以在实际工作中这样直接获取不太好,如果遇到了不存在的key程序会报错,导致程序异常退出。
那是不是可以考虑在获取key的值之前,先判断key是否存在
可以使用contains函数检查key是否存在、
使用if-else语句,如果指定的key不存在,则返回一个默认值

2.
scala> val age = if (ages.contains("jack1")) ages("jack1") else 0
age: Int = 0
1
2
3
4
5
6
7
这样是没问题的,就是写起来有点麻烦了,有没有方便一点的用法呢?
map中还有一个getOrElse函数
3.
scala> val age = ages.getOrElse("jack1", 0)
age: Int = 0

建议后期从map中获取数据都使用这个 getOrElse 函数
修改
1
2
1.更新map中的元素(首先确保创建时使用的是mutable)
scala> ages("jack") = 31
1
2
3
2.增加多个元素
scala> ages += ("hehe" -> 35, "haha" -> 40)
res105: ages.type = Map(hehe -> 35, jessic -> 23, jack -> 31, tom -> 25, haha
1
2
3
3.移除元素
scala> ages -= "hehe"
res106: ages.type = Map(jessic -> 23, jack -> 31, tom -> 25, haha -> 40)
遍历
1
2
3
4
5
6
1.遍历map的entrySet
scala> for ((key, value) <- ages) println(key + " " + value)
jessic 23
jack 31
tom 25
haha 40
1
2
3
4
5
6
2.遍历map的key
scala> for (key <- ages.keySet) println(key)
jessic
jack
tom
haha
1
2
3
4
5
6
3.遍历map的value
scala> for (value <- ages.values) println(value)
23
31
25
40
子类HashMap
1
2
3
HashMap:是一个按照key的hash值进行排列存储的map

HashMap分为可变和不可变的,没有什么特殊之处
子类SortedMap
1
SortedMap:可以自动对Map中的key进行排序【有序的map】
1
2
3
4
5
在这主要演示一下SortedMap和LinkedHashMap
SortedMap是不可变的

scala> val ages = scala.collection.immutable.SortedMap("b" -> 30, "a" -> 15,
ages: scala.collection.immutable.SortedMap[String,Int] = Map(a -> 15, b -> 30
子类LinkedHashMap
1
2
3
LinkedHashMap:可以记住插入的key-value的顺序

LinkedHashMap是可变的
1
2
3
4
5
6
7
scala> val ages = new scala.collection.mutable.LinkedHashMap[String, Int]()
ages: scala.collection.mutable.LinkedHashMap[String,Int] = Map()
scala> ages("b")=30
scala> ages("a")=15
scala> ages("c")=25
scala> ages
res116: scala.collection.mutable.LinkedHashMap[String,Int] = Map(b -> 30, a
Array
1
2
3
4
Scala中Array的含义与Java中的数组类似,长度不可变
由于Scala和Java都是运行在JVM中,双方可以互相调用,因此Scala数组的底层实际上就是Java数组

数组初始化后,长度就固定下来了,而且元素全部根据其类型进行初始化
1
2
3
4
5
6
7
scala> val a = new Array[Int](10)
a: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
scala> a(0)
res65: Int = 0
scala> a(0)=1
scala> a(0)
res67: Int = 1
1
2
3
4
5
6
7
8
也可以直接使用Array()创建数组,元素类型自动推断
scala> val a = Array("hello", "world")
a: Array[String] = Array(hello, world)

scala> a(0)
res68: String = hello
scala> val a1 = Array("hello", 30)
a1: Array[Any] = Array(hello, 30)
ArrayBuffer
1
2
3
4
5
6
7
8
9
如果想使用一个长度可变的数组,就需要使用到ArrayBuffer了

Scala中ArrayBuffer与Java中的ArrayList类似,长度可变

ArrayBuffer:添加元素、移除元素
如果不想每次都使用全限定名,则可以预先导入ArrayBuffer类

scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer
1
2
3
4
5
使用ArrayBuffer()的方式可以创建一个空的ArrayBuffer

注意:也支持直接创建并且初始化ArrayBuffer(1,2,3,4)

scala> val b = new ArrayBuffer[Int]()
添加元素
1
2
3
4
5
1.使用+=操作符,可以添加一个元素,或者多个元素
b += 1 或者 b += (2, 3, 4, 5)

2.使用insert()函数可以在指定位置插入元素,但是这种操作效率很低,因为需要移动指定位置后的所有元素 向3号角标的位置添加一个元素 30
scala> b.insert(3,30)
移除元素
1
2
3
4
使用 remove() 函数可以移除指定位置的元素
移除1号角标的元素
scala> b.remove(1)
res73: Int = 2
Array和ArrayBuffer转化
1
2
b.toArray:ArrayBuffer转Array
a.toBuffer:Array转ArrayBuffer
数组常见操作
1
2
3
4
下面看一下针对数据的常见操作

遍历Array和ArrayBuffer的两种方式
由于Array和ArrayBuffer都是有角标的,所以在迭代数组中元素的时候除了可以使用前面迭代集合的方式还可以使用角标迭代
遍历
1
2
3
4
5
6
1.scala> for(i <- b) println(i)
1 2 3 4 5


2.scala> for(i <- 0 until b.length ) println(b(i))
1 2 3 4 5
求和,最大值
1
2
3
4
scala> val sum = a.sum
sum: Int = 15
scala> val max = a.max
max: Int = 5
排序
1
scala> scala.util.Sorting.quickSort(a)
Tuple
1
2
3
4
5
Tuple:称之为元组,它与Array类似,都是不可变的,但与数组不同的是元组可以包含不同类型的元素

Tuple中的元素角标从 1 开始

注意:目前 Scala 支持的元组最大长度为 22 ,对于更大长度可以使用集合或数组
1
2
3
4
5
6
scala> val t = (1, 3.14, "hehe")
t: (Int, Double, String) = (1,3.14,hehe)
scala> t._1
res117: Int = 1
scala> t._3
res118: String = hehe
总结
1
2
3
4
5
6
7
8
9
前面讲了很多集合体系中的数据结构,有的是可变的,有的是不可变的,有的是既是可变的又是不可变的,听起来有点乱,在这里我们总结一下
可变集合: LinkedHashSet、ListBuffer、ArrayBuffer、LinkedHashMap
不可变集合: List、SortedMap
可变+不可变集合: Set、HashSet、SortedSet、Map、HashMap

还有两个编外人员:
Array、Tuple
Array:长度不可变,里面的元素可变
Tuple:长度不可变,里面的元素也不可变

Scala中函数的使用

函数的定义

1
2
3
4
5
6
先来看一下函数的定义
在Scala中定义函数需要使用 def 关键字,函数包括函数名、参数、函数体

Scala要求必须给出函数所有参数的类型,但是函数返回值的类型不是必须的,因为Scala可以自己根据函数体中的表达式推断出返回值类型。

函数中最后一行代码的返回值就是整个函数的返回值,不需要使用return,这一点与Java不同,java中函数的返回值是必须要使用return的
1
2
3
4
5
单行函数
scala> def sayHello(name: String) = print("Hello, " + name)
sayHello: (name: String)Unit
scala> sayHello("Scala")
Hello, Scala
1
2
3
4
5
6
7
8
9
10
11
12
多行函数
scala> :paste
// Entering paste mode (ctrl-D to finish)
def sayHello(name: String, age: Int) = {
println("My name is "+name+",age is "+age)
age
}
// Exiting paste mode, now interpreting.
sayHello: (name: String, age: Int)Int
scala> sayHello("Scala",18)
My name is Scala,age is 18
res120: Int = 18

函数的参数

默认参数
1
2
3
在Scala中,有时候我们调用某些函数时,不希望给出参数的具体值,而是希望使用参数自身默认的值,此时就需要在定义函数时使用默认参数。

如果给出的参数不够,则会从左往右依次应用参数。
1
2
3
4
scala> def sayHello(fName: String, mName: String = "mid", lName: String = "la
sayHello: (fName: String, mName: String, lName: String)String
scala> sayHello("zhang","san")
res122: String = zhang san last
带名参数
1
2
3
4
5
在调用函数时,也可以不按照函数定义的参数顺序来传递参数,而是使用带名参数的方式来传递。
scala> def sayHello(fName: String, mName: String = "mid", lName: String = "la
sayHello: (fName: String, mName: String, lName: String)String
scala> sayHello(fName = "Mick", lName = "Tom", mName = "Jack")
res127: String = Mick Jack Tom
可变参数
1
在Scala中,有时我们需要将函数定义为参数个数可变的形式,则此时可以使用变长参数来定义函数
1
2
3
4
5
6
7
8
9
10
11
scala> :paste
// Entering paste mode (ctrl-D to finish)
def sum(nums: Int*) = { //Int的I要大写
var res = 0
for (num <- nums) res += num
res
}
// Exiting paste mode, now interpreting.
sum: (nums: Int*)Int
scala> sum(1,2,3,4,5)
res129: Int = 15

特殊的函数-过程

1
2
3
4
5
在Scala中,定义函数时,如果函数体直接在花括号里面而没有使用=连接,则函数的返回值类型就是Unit,这样的函数称之为过程

过程通常用于不需要返回值的函数
过程还有一种写法,就是将函数的返回值类型显式定义为Unit
比较一下这四种写法的区别
1
2
3
4
5
6
7
8
9
10
有返回值
def sayHello(name: String) = "Hello, " + name
def sayHello(name: String): String = "Hello, " + name

无返回值
def sayHello(name: String) { "Hello, " + name }
def sayHello(name: String): Unit = "Hello, " + name

前面两种写法的效果是一样的,都是函数
后面两种写法的效果是一样的,都是过程

lazy

1
2
3
4
Scala提供了lazy特性,如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算

什么场景下需要使用lazy特性呢?
这种特性对于特别耗时的操作特别有用,比如打开文件这个操作。
1
2
3
4
5
6
7
8
9
scala> import scala.io.Source._
import scala.io.Source._
scala> lazy val lines = fromFile("D://test.txt").mkString
lines: String = <lazy>

即使D://test.txt文件不存在,代码也不会报错,只有变量使用时才会报错,这就是lazy这个特性
scala> lines
java.io.FileNotFoundException: D:\test.txt (系统找不到指定的文件。)
.......

用法积累

1
2
3
4
5
1.字符串不能用单引号代替双引号
2.过程不用等号那种形式时,语句只能放在花括号里
3.数据类型Int的I只能大写
4.只能lazy val xxx;不能lazy var
5

本文标题:大数据开发工程师-第九周 第2章 Scala基础语法

文章作者:TTYONG

发布时间:2022年02月28日 - 14:02

最后更新:2023年03月29日 - 22:03

原始链接:http://tianyong.fun/%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88-%E7%AC%AC%E4%B9%9D%E5%91%A8-%E7%AC%AC2%E7%AB%A0-Scala%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95.html

许可协议: 转载请保留原文链接及作者。

多少都是爱
0%