第二章:Scala基础

目录

一:Scala定义以及特性

(1)Scala定义:

        Scala是一种基于Java虚拟机(JVM)的编程语言,它结合了面向对象编程(OOP)和函数式编程(FP)的最佳特性。Scala的设计旨在提供一种简洁、高效、类型安全且具备强大表达能力的编程语言,使得开发者能够以更少的代码行进行高效的编程。

(2)Scala特性:

a:面向对象:是一种存粹的面向对象语言,支持类和对象的定义,以及继承、多态和封装等面向对象的特性。

b:函数式编程:支持高阶函数、匿名函数(Lambda表达式)、柯里化等函数式编程特性。

c:静态类型:Scala是一种静态类型语言,意味着类型是在编译时就确定的。

d:可扩展

二:Scala安装

 三:Scala方法

一:数据类型

Scala常用数据类型

字符串转换为整数

整数转换为浮点数 

浮点数转换为双精度浮点数

其他数据类型转换

隐式类型转换

使用asInstanceOf进行类型转换

总结

二:定义与使用常量、变量

常量:

变量

三:使用运算符:

算术运算符

关系运算符

逻辑运算符

位运算符

赋值运算符 

 四:定义与使用数组

数组

是Scala中常用的一种数据结构,数组是一种存储了相同类型元素的固定大小的顺序集合。

 Scala

定义一个数组的语法格式如下。

# 第1种方式

var arr: Array[String] = new Array[String](num)

# 第2种方式

var arr:Array[String] = Array(元素1,元素2,…)

数组常用的方法

 五:定义与使用函数

1.匿名函数

匿名函数即在定义函数时不给出函数名的函数。

Scala中匿名函数是使用箭头“=>”定义的,箭头的左边是参数列表,箭头的右边是表达式,表达式将产生函数的结果。

通常可以将匿名函数赋值给一个常量或变量,再通过常量名或变量名调用该函数。

若函数中的每个参数在函数中最多只出现一次,则可以使用占位符“_”代替参数。

2.高阶函数—函数作为参数

高阶函数指的是操作其他函数的函数。

高阶函数可以将函数作为参数,也可以将函数作为返回值。

高阶函数经常将只需要执行一次的函数定义为匿名函数并作为参数。一般情况下,匿名函数的定义是“参数列表=>表达式”。

由于匿名参数具有参数推断的特性,即推断参数的数据类型,或根据表达式的计算结果推断返回结果的数据类型,因此定义高阶函数并使用匿名函数作为参数时,可以简化匿名函数的写法。

3.高阶函数—函数作为返回值 

高阶函数可以产生新的函数,并将新的函数作为返回值。

定义高阶函数计算矩形的周长,该函数传入一个Double类型的值作为参数,返回以一个Double类型的值作为参数的函数,如下图。

 4.函数柯里化函数

柯里化是指将接收多个参数的函数变换成接收单一参数(最初函数的第一个参数)的函数,新的函数返回一个以原函数余下的参数为参数的函数。

定义两个整数相加的函数,一般函数的写法及其调用方式如下图。

​编辑使用函数柯里化

六: 使用if判断

Scala中的if判断根据复杂程度可分为

if语句、if…else语句、if…else if…else语句、if…else嵌套语句,语法格式如下。

# if语句

if(布尔表达式) { 若布尔表达式为true,则执行该语句块}

# if…else语句

if(布尔表达式) { 若布尔表达式为true,则执行该语句块}

else { 若布尔表达式为false,则执行该语句块}

# if…else if…else语句

if(布尔表达式1) {

   若布尔表达式1为true,则执行该语句块

} else if(布尔表达式2) {

      若布尔表达式2为true,则执行该语句块

} else if(布尔表达式3) {

      若布尔表达式3为true,则执行该语句块

}else {

      若以上布尔表达式都为false,则执行该语句块

# if…else嵌套语句

if (布尔表达式1) {

   if(布尔表达式2) {      若布尔表达式2为true,则执行该语句块

   } else if(布尔表达式3) {      若布尔表达式3为true,则执行该语句块

   } else {      若布尔表达式2为false且布尔表达式3为false,则执行该语句块

   }

} else {    若以上条件都为false,则执行该语句块 

}

七:使用for循环

循环是指在某种条件下将一段代码按顺序重复执行。在Scala中有3种循环结构,分别为while循环、do…while循环和for循环。

or循环是相对较为常用的一种循环,for循环语法格式如下。

for(变量<- 集合) {循环语句}

多重循环是常见的for循环,多重循环也称为for循环嵌套,是指在两个或多个区间内循环反复,多个循环区间用分号隔开。

Scala可以在for循环中使用if判断过滤一些元素,多个过滤条件用分号隔开。

for循环使用yield可以将返回值作为一个变量存储,语法格式如下。

var retVar = for(var x <- List; if condition1; if condition2…) yield x

retVar是变量名,for关键字后的括号用于指明变量和条件,而yield会将每一次循环得到的返回值保存在一个集合中,循环结束后将返回该集合,并赋值给变量retVar。

使用yield对1~10的偶数进行记录,并保存至变量even中,如下图。

八:定义与使用列表

ØScala的列表(List)与数组非常相似,列表的所有元素都具有相同的类型。

Ø与数组不同的是,列表是不可变的,即列表的元素不能通过赋值进行更改。

Ø定义列表时,需要写明列表元素的数据类型,或者根据列表初值类型自动推断。具有类型T的元素的列表类型可写为List[T]。

Ø构造列表的两个基本单位是“Nil”和“::”。

•“Nil”可以表示空列表;

•“::”称为中缀操作符,表示列表从前端扩展,遵循右结合。

列表操作常用方法 

创建和操作列表

 九:定义与使用集合

Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。

集合操作常用方法

Scala合并两个列表时使用的是:::()或concat()方法,而合并两个集合使用的是++()方法。 

不可变集合的合并

使用++操作符

使用union方法

可变集合的合并

使用++操作符

使用union方法

注意事项

 十:定义与使用映射

Ø映射(Map)是一种可迭代的键值对结构。

Ø所有的值都可以通过键来获取,并且映射中的键都是唯一的。

Ø集合操作常用方法同样也适合映射。

Ø另外映射还可以通过keys方法获取所有的键,通过values方法获取所有值,也可以通过isEmpty方法判断映射的数据是否为空

不可变映射的使用

示例代码

可变映射的使用

示例代码

十一:定义与使用元组

Ø元组(Tuple)是一种类似于列表的结构,但与列表不同的是,元组可以包含不同类型的元素。

Ø元组的值是通过将单个的值包含在圆括号中构成的。

Ø目前,Scala支持的元组最大长度为22,即Scala元组最多只能包含22个元素。

Ø访问元组元素可以通过“元组名称._元素索引”进行,索引从1开始。

Ømap()方法可通过一个函数重新计算列表中的所有元素,并且返回一个包含相同数目元素的新列表。

 Øforeach()方法和map()方法类似,但是foreach()方法没有返回值,只用于对参数的结果进行输出。

Øfilter()方法可以移除传入函数的返回值为false的元素。

 Øflatten()方法可以将嵌套的结构展开,即flatten()方法可以将一个二维的列表展开成一个一维的列表。

ØflatMap()方法结合了map()方法和flatten()方法的功能,接收一个可以处理嵌套列表的函数,再对返回结果进行连接。

ØgroupBy()方法可对集合中的元素进行分组操作,返回的结果是一个映射。

 ​编辑

十二:定义Scala类

        ØScala是一种纯粹的面向对象语言,面向对象语言有两个重要的概念:类和对象。

Ø其中,类是对象的抽象,也可以把类理解为模板,对象才是真正的实体。

Ø一般定义类的格式如下。

class ClassName(参数列表) extendst {}

Ø一个Scala源文件中可以有多个类,并且Scala类可以有参数。

Ø一般,Scala类名的第一个字母需要大写,如果需要使用几个单词构成一个类的名称,那么每个单词的第一个字母都要大写。

Ø与Java等其他语言不同的是,Scala中的类不定义为public。

ØScala类继承一个类时需要使用关键字extends。

ØScala只允许继承一个父类,并且继承父类的所有属性和方法。

Ø子类继承父类中已经实现的方法时,需要使用override关键字,子类继承父类中未实现的方法时,可以不用override关键字。

 十三:使用Scala单例模式

ØScala中没有static关键字,因此Scala的类中不存在静态成员。但是Scala可以使用object关键字实现单例模式。

ØScala中使用单例模式时需要使用object定义一个单例对象(object对象),单例对象在整个程序中只有一个实例。单例对象与类的区别在于单例对象不能带参数。

Ø定义单例对象的语法如下。s

scala中实现单例模式的方法

使用object关键字

使用伴生对象

object ObjectName {}

Ø包含main方法的object对象可以作为程序的入口点。

Ø当单例对象与某个类共享同一个名称时,单例对象被称作这个类的伴生对象,类被称为这个单例对象的伴生类。类和它的伴生对象可以互相访问对方的私有成员。

Ø需要注意的是,必须在同一个源文件里定义类和它的伴生对象。

十四:使用Scala模式匹配

ØScala提供了强大的模式匹配机制。一个模式匹配包含了一系列备选项,每个都开始于关键字case。每个备选项都包含了一个模式及一到多个表达式。模式和表达式之间用“=>”隔开。

Ø在Scala中,使用case关键字定义的类称为样例类。样例类是一种特殊的类,经过优化可应用于模式匹配。

ØScala编译器为样例类添加了一些语法上的便捷设定,具体如下。

•在伴生对象中提供了apply()方法,因此不使用new关键字也可以构造对象。

•样例类参数列表中的所有参数已隐式获得val关键字。

•编译器为样例类添加了toString()、hashCode()和equals()等方法。

 十五:读写文件

Ø在实际的实践中,常常需要涉及文件的读写操作。

ØScala不提供任何特殊文件写入能力,所以进行文件的写操作使用的是Java的I/O类中的PrintWriter来实现。

Ø有时候需要接收用户在屏幕上输入的指令来处理程序,这时可以使用StdIn.readLine接收用户输入的指令。用户可以在REPL中直接输入“StdIn.readLine”命令并按“Enter”键,并在屏幕上输入指令;也可以将StdIn.readLine赋值给一个变量,用户在屏幕上输入的内容即可赋值给该变量。

Ø用户可以使用Scala的Source类读取文件内容。


一:Scala定义以及特性

(1)Scala定义:

        Scala是一种基于Java虚拟机(JVM)的编程语言,它结合了面向对象编程(OOP)和函数式编程(FP)的最佳特性。Scala的设计旨在提供一种简洁、高效、类型安全且具备强大表达能力的编程语言,使得开发者能够以更少的代码行进行高效的编程。

(2)Scala特性:

a:面向对象:是一种存粹的面向对象语言,支持类和对象的定义,以及继承、多态和封装等面向对象的特性。

b:函数式编程:支持高阶函数、匿名函数(Lambda表达式)、柯里化等函数式编程特性。

c:静态类型:Scala是一种静态类型语言,意味着类型是在编译时就确定的。

d:可扩展

二:Scala安装

a:在网页上运行Scala

1. 通过浏览器查找 Scastie 并进入,即可进入 Scala 在线运行环境。
2. 进入 Scastie 界面后,在上窗格中输入“ println ("hello world")”
3. 单击“ Run” 按钮,输出信息将显示在下窗格中,如下图。 

b:Scala环境设置

1: Scala 运行环境众多,可以运行在 Windows Linux macOS 等系统上。 Scala 是运行在 JVM 上的语言,因此必须确保系统环境中安装了 JDK ,即 Java 开发工具包,而且必须确保 JDK 版本与本书安装的 Spark JDK 编译版本一致,本书中使用的 JDK JDK 8 Java 1.8 )。
2: 查看 Java 版本.

c:Scala安装 

1)在LinuxmacOS系统上安装Scala

Scala 官网 下载 Scala 安装包,安装包名称为“ scala-2.12.15.tgz
将其上传至 /opt 目录
解压安装包至 / usr /local 目录下
配置 Scala 环境变量

2)在Windows系统上安装Scala

Scala 官网下载 Scala 安装包,安装包名称为“ scala.msi
双击 scala.msi 安装包,开始安装软件。
进入欢迎界面,单击右下角的“ Next” 按钮后出现许可协议选择提示框,选择接受许可协议中的条款并单击右下角的“ Next” 按钮。
选择安装路径,本文Scala的安装路径选择在非系统盘的“ D:\Program Files ( x86 )\spark\ scala \” ,单击“ OK” 按钮进入安装界面。
在安装界面中单击右下角的“ Install” 按钮进行安装,安装完成时单击“ Finish” 按钮完成安装。
右键单击“此电脑”图标,选择“属性”选项,在弹出的窗口中选择“高级系统设置”选项。在弹出的对话框中选择“高级”选项卡,并单击“环境变量”按钮,在环境变量对话框中,选择“ Path” 变量并单击“编辑”按钮,在 Path 变量中添加 Scala 安装目录的 bin 文件夹所在路径,如“ D:\Program Files ( x86 )\spark\ scala \bin”

 d:运行Scala

Scala 解释器也称为 REPL Read-Evaluate-Print-Loop ,读取 - 执行 - 输出 - 循环)。
在命令行中输入“ scala ,即可进入 REPL ,如下图。
REPL是一个交互式界面,用户输入命令时,可立即产生交互反馈。
输入“ :quit” 命令即可退出 REPL ,如下图。
右图是一个 Scala 类,该类实现了两个数相加的方法。。
如果要使用该方法,那么需要通过 import 加载该方法,如左图,其中, add 是类名, addInt 是方法名。

 三:Scala方法

一:数据类型

Scala常用数据类型

数据类型

描述

Int

32位有符号补码整数。数值区间为−3276832767

Float

32IEEE754IEEE浮点数算术标准)单精度浮点数

Double

64IEEE754IEEE浮点数算术标准)双精度浮点数

String

字符序列,即字符串

Boolean

布尔值,truefalse

Unit

表示无值,作用与Java中的void一样,是不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()

在Scala中,数据类型转换是一个常见的操作,它支持多种数据类型之间的转换,如字符串到整数、整数到浮点数、浮点数到双精度浮点数等。接下来,我将详细介绍Scala中常见的数据类型转换方法,并提供相应的代码示例。

字符串转换为整数

在Scala中,可以使用toInt方法将字符串转换为整数。例如,若要将字符串"123"转换为整数,可以使用以下代码:

val str = "123"
val num = str.toInt
println(num) // 输出:123

运行结果:

如果字符串不包含整数,则会抛出NumberFormatException异常。为了避免这个异常,可以使用trycatch来捕获异常,并进行相应处理。 

整数转换为浮点数 

若要将整数转换为浮点数,可以使用toDouble方法。例如,将整数123转换为浮点数123.0

val num = 123
val doubleNum = num.toDouble
println(doubleNum) // 输出:123.0

运行结果:

 

浮点数转换为双精度浮点数

对于浮点数到双精度浮点数的转换,同样使用toDouble方法即可。例如,将123.45转换为123.45

val floatNum = 123.45
val doubleNum = floatNum.toDouble
println(doubleNum) // 输出:123.45

运行结果: 

其他数据类型转换

Scala还支持其他类型的转换,如toFloattoLongtoShorttoBytetoChar等。例如,将整数123转换为浮点数123.0

val num = 123
val floatNum = num.toFloat
println(floatNum) // 输出:123.0

运行结果:

将十六进制字符串转换为长整数Long类型:

val hexString = "1A3B"
val longNum = hexString.toLong
println(longNum) // 输出:10995

隐式类型转换

Scala还支持隐式类型转换,这可以在不需要显式类型转换方法的情况下,自动将一种类型转换为另一种类型。例如,隐式地将String类型转换为Int类型:

val str = "123"
val num = implicitly[Int](str)
println(num) // 输出:123

使用asInstanceOf进行类型转换

asInstanceOf方法可以将一个值强制转换为指定的类型。例如,将Double类型转换为Int类型:

val doubleNum = 3.14
val intNum = doubleNum.asInstanceOf[Int]
println(intNum) // 输出:3

总结

Scala提供了丰富的数据类型转换方法,使得在不同类型之间进行转换变得十分方便。在使用这些方法时,需要注意可能会出现的类型不匹配问题,并合理使用异常处理机制来保证程序的健壮性。

object Test {
   def main(args: Array[String]) = {
      var a = 10;
      var b = 20;
      var c = 25;
      var d = 25;
      println("a + b = " + (a + b) );
      println("a - b = " + (a - b) );
      println("a * b = " + (a * b) );
      println("b / a = " + (b / a) );
      println("b % a = " + (b % a) );
      println("c % a = " + (c % a) );
      
   }
}在实际的编程实践中,可以根据具体的需求选择合适的数据类型转换方法。

二:定义与使用常量、变量

常量:

在程序运行过程中值不会发生变化的量为常量或值,常量通过val关键字定义,常量一旦定义就不可更改,即不能对常量进行重新计算或重新赋值。定义一个常量的语法格式如下。

val name: type = initialization

变量

变量是在程序运行过程中值可能发生改变的量。变量使用关键字var定义。与常量不同的是,变量定义之后可以重新被赋值。定义一个变量的语法格式如下。

var name: type = initialization

三:使用运算符:

运算符

意义

示例

算术

运算符

+

两个数相加

1+21.+(2)

两个数相减

1−21. − (2)

*

两个数相乘

1*21.*(2)

/

两个数相除

1/21./(2)

%

两个数取余

1%21.%(2)

关系

运算符

判断左值是否大于右值,是则结果为真,否则结果为假

1>21.>(2)

判断左值是否小于右值,是则结果为真,否则结果为假

1<21.<(2)

>=

判断左值是否大于等于右值,是则结果为真,否则结果为假

1>=21.>=(2)

<=

判断左值是否小于等于右值,是则结果为真,否则结果为假

1<=21.<=(2)

==

判断左值是否等于右值,是则结果为真,否则结果为假

1==21.==(2)

!=

判断左值是否不等于右值,是则结果为真,否则结果为假

1!=21.!=(2)

逻辑

运算符

&&

若两个条件成立则结果为真,否则结果为假

1>2 && 2>31>2.&&(2>3)

||

两个条件有一个成立则结果为真,否则结果为假

1>2 || 2>31>2.||(2>3)

对当前结果取反

!(1>2)

运算符

&

参加运算的两个数据,按二进制位进行&运算,两位同时结果为1结果才为1,否则为0

0 & 10.&(1)

|

参加运算的两个数据,按二进制位进行|运算,两位只要有一个为1则结果为1

0 | 10.|(1)

^

参加运算的两个数据,按二进制位进行^运算,两位不同时结果为1,相同时结果为0

0^10.^(1)

赋值

运算符

=

将右侧的值赋于左侧

val a = 2

+=

执行加法后再赋值左侧

a += 2

−=

执行减法后再赋值左侧

a-= 1

*=

执行乘法后再赋值左侧

a *= 2

/=

执行除法后再赋值左侧

a /= 3

%=

执行取余后再赋值左侧

a %= 5

<<=

左移位后赋值左侧

a <<= 2

>>=

右移位后赋值左侧

a >>= 2

&=

按位&运算后赋值左侧

a &= 2

|=

按位|运算后赋值左侧

a |= 2

^=

按位^运算后赋值左侧

a ^= 2

算术运算符

下表列出了 Scala 支持的算术运算符。

假定变量 A 为 10,B 为 20:

object Test {
   def main(args: Array[String]) = {
      var a = 10;
      var b = 20;
      var c = 25;
      var d = 25;
      println("a + b = " + (a + b) );
      println("a - b = " + (a - b) );
      println("a * b = " + (a * b) );
      println("b / a = " + (b / a) );
      println("b % a = " + (b % a) );
      println("c % a = " + (c % a) );
      
   }
}

关系运算符

下表列出了 Scala 支持的关系运算符。

假定变量 A 为 10,B 为 20:

object Test {
   def main(args: Array[String]) = {
      var a = 10;
      var b = 20;
      println("a == b = " + (a == b) );
      println("a != b = " + (a != b) );
      println("a > b = " + (a > b) );
      println("a < b = " + (a < b) );
      println("b >= a = " + (b >= a) );
      println("b <= a = " + (b <= a) );
   }
}

逻辑运算符

下表列出了 Scala 支持的逻辑运算符。

假定变量 A 为 1,B 为 0:

object Test {
   def main(args: Array[String]) = {
      var a = true;
      var b = false;

      println("a && b = " + (a&&b) );

      println("a || b = " + (a||b) );

      println("!(a && b) = " + !(a && b) );
   }
}

位运算符

位运算符用来对二进制位进行操作,~,&,|,^ 分别为取反,按位与,按位或,按位异或运算,如下表实例:

A = 0011 1100

B = 0000 1101

-------位运算----------

A&B = 0000 1100

A|B = 0011 1101

A^B = 0011 0001

~A  = 1100 0011
object Test {
   def main(args: Array[String]) = {
      var a = 60;           /* 60 = 0011 1100 */  
      var b = 13;           /* 13 = 0000 1101 */
      var c = 0;

      c = a & b;            /* 12 = 0000 1100 */ 
      println("a & b = " + c );

      c = a | b;            /* 61 = 0011 1101 */
      println("a | b = " + c );

      c = a ^ b;            /* 49 = 0011 0001 */
      println("a ^ b = " + c );

      c = ~a;               /* -61 = 1100 0011 */
      println("~a = " + c );

      c = a << 2;           /* 240 = 1111 0000 */
      println("a << 2 = " + c );

      c = a >> 2;           /* 15 = 1111 */
      println("a >> 2  = " + c );

      c = a >>> 2;          /* 15 = 0000 1111 */
      println("a >>> 2 = " + c );
   }
}

赋值运算符 

object Test {
   def main(args: Array[String]) = {
      var a = 10;       
      var b = 20;
      var c = 0;

      c = a + b;
      println("c = a + b  = " + c );

      c += a ;
      println("c += a  = " + c );

      c -= a ;
      println("c -= a = " + c );

      c *= a ;
      println("c *= a = " + c );

      a = 10;
      c = 15;
      c /= a ;
      println("c /= a  = " + c );

      a = 10;
      c = 15;
      c %= a ;
      println("c %= a  = " + c );

      c <<= 2 ;
      println("c <<= 2  = " + c );

      c >>= 2 ;
      println("c >>= 2  = " + c );

      c >>= a ;
      println("c >>= a  = " + c );

      c &= a ;
      println("c &= 2  = " + c );
     
      c ^= a ;
      println("c ^= a  = " + c );

      c |= a ;
      println("c |= a  = " + c );
   }
}

运算符优先级取决于所属的运算符组,它会影响算式的的计算。

实例: x = 7 + 3 * 2; 这里, x 计算结果为 13, 而不是 20,因为乘法(*) 高于加法(+), 所以它先计算 3*2 再加上 7。

查看以下表格,优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级。

类别运算符关联性
1() []左到右
2! ~右到左
3* / %左到右
4+ -左到右
5>> >>> <<左到右
6> >= < <=左到右
7== !=左到右
8&左到右
9^左到右
10|左到右
11&&左到右
12||左到右
13= += -= *= /= %= >>= <<= &= ^= |=右到左
14,左到右

 四:定义与使用数组

数组

Scala中常用的一种数据结构,数组是一种存储了相同类型元素的固定大小的顺序集合。

 Scala

定义一个数组的语法格式如下。

# 1种方式

var arr: Array[String] = new Array[String](num)

# 2种方式

var arr:Array[String] = Array(元素1,元素2,…)

数组常用的方法

方法

描述

length

返回数组的长度

head

查看数组的第一个元素

tail

查看数组中除了第一个元素外的其他元素

isEmpty

判断数组是否为空

contains(x)

判断数组是否包含元素x

代码:

object p2 {
  def main(args: Array[String]): Unit = {
    // 字符串数组
    val strArray = Array("Hello", "World", "Scala")
    println(strArray.length) // 输出:3
    println(strArray.head) // 输出:hello
    println(strArray.tail.mkString("Array(", ", ", ")")) // 输出:world,scala
    println(strArray.isEmpty) // 输出:false
    println(strArray.contains("hello")) // 输出:false


    // 整数数组
    val intArray = Array(1, 2, 3)
    println(intArray.length) // 输出:3

    // 使用`size`方法可能会报错
    val list = List(1, 2, 3)
    println(list.size) // 报错:Int does not take parameters
  }

}

 运行结果:

 五:定义与使用函数

1.匿名函数

匿名函数即在定义函数时不给出函数名的函数。

Scala中匿名函数是使用箭头“=>”定义的,箭头的左边是参数列表,箭头的右边是表达式,表达式将产生函数的结果。

通常可以将匿名函数赋值给一个常量或变量,再通过常量名或变量名调用该函数。

若函数中的每个参数在函数中最多只出现一次,则可以使用占位符“_”代替参数。

def main(args: Array[String]): Unit = {
    // 使用匿名函数将列表中的每个元素平方
    val numbers = List(1, 2, 3, 4, 5)
    numbers.map((x: Int) => x * 2)
    val squaredNumbers = numbers.map((x: Int) => x * x)
    // 输出结果
    squaredNumbers.foreach(println)
  }

2.高阶函数函数作为参数

高阶函数指的是操作其他函数的函数。

高阶函数可以将函数作为参数,也可以将函数作为返回值。

高阶函数经常将只需要执行一次的函数定义为匿名函数并作为参数。一般情况下,匿名函数的定义是“参数列表=>表达式”。

由于匿名参数具有参数推断的特性,即推断参数的数据类型,或根据表达式的计算结果推断返回结果的数据类型,因此定义高阶函数并使用匿名函数作为参数时,可以简化匿名函数的写法。

import scala.collection.immutable.List

object HighOrderFunctions {
  def main(args: Array[String]): Unit = {
    // 高阶函数的定义
    val doubleSalary: Int => Int = (x: Int) => x * 2
    // 使用高阶函数对薪资列表进行映射
    val newSalaries: List[Int] = List(20000, 70000, 40000).map(doubleSalary)
    // 输出结果
    newSalaries.foreach(println)
  }
}

3.高阶函数函数作为返回值 

高阶函数可以产生新的函数,并将新的函数作为返回值。

定义高阶函数计算矩形的周长,该函数传入一个Double类型的值作为参数,返回以一个Double类型的值作为参数的函数,如下图。

 4.函数柯里化函数

柯里化是指将接收多个参数的函数变换成接收单一参数(最初函数的第一个参数)的函数,新的函数返回一个以原函数余下的参数为参数的函数。

定义两个整数相加的函数,一般函数的写法及其调用方式如下图。

使用函数柯里化

六: 使用if判断

Scala中的if判断根据复杂程度可分为

if语句、if…else语句、if…else if…else语句、if…else嵌套语句,语法格式如下。

# if语句

if(布尔表达式) { 若布尔表达式为true,则执行该语句块}

if (x == 30) {
  if (y == 10) {
    println("X = 30 and Y = 10")
  }
}

# if…else语句

if(布尔表达式) { 若布尔表达式为true,则执行该语句块}

else { 若布尔表达式为false,则执行该语句块}

val number = 15
if (number > 10) {
  println("Number is greater than 10")
} else {
  println("Number is not greater than 10")
}

# if…else if…else语句

if(布尔表达式1) {

   若布尔表达式1true,则执行该语句块

} else if(布尔表达式2) {

      若布尔表达式2true,则执行该语句块

} else if(布尔表达式3) {

      若布尔表达式3true,则执行该语句块

}else {

      若以上布尔表达式都为false,则执行该语句块

val score = 85
if (score >= 90) {
  println("Excellent score!")
} else if (score >= 80) {
  println("Good score!")
} else if (score >= 60) {
  println("Passed score!")
} else {
  println("Failed score.")
}

# if…else嵌套语句

if (布尔表达式1) {

   if(布尔表达式2) {      若布尔表达式2true,则执行该语句块

   } else if(布尔表达式3) {      若布尔表达式3true,则执行该语句块

   } else {      若布尔表达式2false且布尔表达式3false,则执行该语句块

   }

} else {    若以上条件都为false,则执行该语句块 

}

val score = 85
if (score >= 90) {
  println("Excellent grade!")
} else if (score >= 75) {
  println("Good grade.")
} else if (score >= 60) {
  println("Passing grade.")
} else {
  println("Failing grade.")
}

七:使用for循环

循环是指在某种条件下将一段代码按顺序重复执行。在Scala中有3种循环结构,分别为while循环、do…while循环和for循环。

or循环是相对较为常用的一种循环,for循环语法格式如下。

for(变量<- 集合) {循环语句}

多重循环是常见的for循环,多重循环也称为for循环嵌套,是指在两个或多个区间内循环反复,多个循环区间用分号隔开。

Scala可以在for循环中使用if判断过滤一些元素,多个过滤条件用分号隔开。

for循环使用yield可以将返回值作为一个变量存储,语法格式如下。

var retVar = for(var x <- List; if condition1; if condition2…) yield x

retVar是变量名,for关键字后的括号用于指明变量和条件,而yield会将每一次循环得到的返回值保存在一个集合中,循环结束后将返回该集合,并赋值给变量retVar

使用yield110的偶数进行记录,并保存至变量even中,如下图。

八:定义与使用列表

ØScala的列表(List)与数组非常相似,列表的所有元素都具有相同的类型。

Ø与数组不同的是,列表是不可变的,即列表的元素不能通过赋值进行更改。

Ø定义列表时,需要写明列表元素的数据类型,或者根据列表初值类型自动推断。具有类型T的元素的列表类型可写为List[T]

Ø构造列表的两个基本单位是“Nil”和“::”

Nil”可以表示空列表;

::”称为中缀操作符,表示列表从前端扩展,遵循右结合。

列表操作常用方法 

方法

描述

def head: A

获取列表的第一个元素

def init:List[A]

返回所有元素,除了最后一个元素

def last:A

获取列表的最后一个元素

def tail:List[A]

返回所有元素,除了第一个元素

def :::(prefix: List[A]): List[A]

在列表开头添加指定列表的元素

def take(n: Int): List[A]

获取列表前n个元素

def contains(elem: Any): Boolean

判断列表是否包含指定元素

创建和操作列表

创建一个列表非常简单,可以直接使用 List() 构造函数,或者使用 :: 方法来递增式构建列表。

val list1 = List(1, 2, 3)
val list2 = 1 :: 2 :: 3 :: Nil

列表是不可变的,这意味着一旦创建了一个列表,就不能更改它的内容。如果需要修改列表,只能创建一个新的列表。

object p6 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4, 5)
    val filteredList = list.filter(num => num % 2 == 0)
    println(filteredList) // 输出: List(2, 4, 6)
    println(filteredList.head)//输出List(1)
    println(filteredList.init)//输出List(1,2,3,4)
    println(filteredList.last )//输出List(5)
    println(filteredList.tail)//输出List(2,3,4,5)
//    println(filteredList.:::(prefix = List[1]))//
    println(filteredList.take(3))//输出List(1,2,3)
    println(filteredList.contains(9))//falase
  }
}

 九:定义与使用集合

Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的

集合操作常用方法

方法

描述

def head: A

获取集合的第一个元素

def init:Set[A]

返回所有元素,除了最后一个

def last:A

获取集合的最后一个元素

def tail:Set[A]

返回所有元素,除了第一个

def ++(elems: A): Set[A]

合并两个集合

def take(n: Int): List[A]

获取列表前n个元素

def contains(elem: Any): Boolean

判断集合中是否包含指定元素

Scala合并两个列表时使用的是:::()concat()方法,而合并两个集合使用的是++()方法。 

不可变集合的合并

不可变集合(如SetListMap等)一旦被创建,就不能被修改,除非创建新的集合。要合并两个不可变集合,可以使用++操作符或union方法。

使用++操作符

val set1 = Set(1, 2, 3)
val set2 = Set(3, 4, 5)
val mergedSet = set1 ++ set2
println(mergedSet) // 输出: Set(1, 2, 3, 4, 5)

在上面的代码中,mergedSet将包含set1set2中所有的唯一元素。

使用union方法

val set1 = Set(1, 2, 3)
val set2 = Set(3, 4, 5)
val mergedSet = set1.union(set2)
println(mergedSet) // 输出: Set(1, 2, 3, 4, 5)

可变集合的合并

可变集合(如mutable.SetListBufferHashMap等)允许在运行时动态添加、删除或修改元素。要合并两个可变集合,同样可以使用++操作符或union方法。

使用++操作符

import scala.collection.mutable

val mutableSet1 = mutable.Set(1, 2, 3)
val mutableSet2 = mutable.Set(3, 4, 5)
val mergedSet = mutableSet1 ++ mutableSet2
println(mergedSet) // 输出: Set(1, 2, 3, 4, 5)

使用union方法

import scala.collection.mutable

val mutableSet1 = mutable.Set(1, 2, 3)
val mutableSet2 = mutable.Set(3, 4, 5)
val mergedSet = mutableSet1.union(mutableSet2)
println(mergedSet) // 输出: Set(1, 2, 3, 4, 5)

注意事项

  • 不可变集合的合并会生成新的集合对象,而原始集合保持不变。
  • 可变集合的合并可以直接在原有集合上进行,更新后的集合仍然是可变的。
  • 当处理大量数据时,考虑到性能优化,可以使用SortedSet代替普通的Set来减少排序开销,或者使用HashSet来减少重复元素检查的开销。
  • 并行集合(Parallel Collections)也可以用来加速合并过程,特别是在多核处理器上。

 十:定义与使用映射

Ø映射(Map是一种可迭代的键值对结构

Ø所有的值都可以通过键来获取,并且映射中的键都是唯一的

Ø集合操作常用方法同样也适合映射。

Ø另外映射还可以通过keys方法获取所有的键,通过values方法获取所有值,也可以通过isEmpty方法判断映射的数据是否为空

不可变映射的使用

不可变映射(Map)适用于不需要修改数据结构的情况。这类映射提供了一些方法来处理映射中的数据,比如 getupdate++ 等等。

示例代码
val immutableMap = Map("a" -> 1, "b" -> 2)
val immutableMap2 = immutableMap + ("c" -> 3)
val immutableMap3 = immutableMap - "a"
val immutableMap4 = immutableMap.updated("d", 4)
val immutableMap5 = immutableMap.withDefaultValue(0)

在上述代码中,immutableMap 是一个不可变映射,我们通过 + 和 - 符号以及 update 方法来添加、删除和修改映射中的键值对。withDefaultValue 方法允许我们为一个映射提供一个默认值,这在处理可能不存在的键时很有用。

可变映射的使用

可变映射(mutable.Map)允许在运行时修改映射的结构。可变映射提供了比不可变映射更为丰富的方法来处理数据。

示例代码
import scala.collection.mutable

val mutableMap = mutable.Map("a" -> 1, "b" -> 2)
mutableMap += ("c" -> 3)
mutableMap -= "a"
mutableMap.put("d", 4)
mutableMap.remove("e")

在这个例子中,我们创建了一个可变映射 mutableMap,并通过 +=-=put 和 remove 方法进行了修改。

十一:定义与使用元组

Ø元组(Tuple)是一种类似于列表的结构,但与列表不同的是,元组可以包含不同类型的元素。

Ø元组的值是通过将单个的值包含在圆括号中构成的

Ø目前,Scala支持的元组最大长度为22,即Scala元组最多只能包含22个元素。

Ø访问元组元素可以通过“元组名称._元素索引”进行,索引从1开始。

Ømap()方法可通过一个函数重新计算列表中的所有元素,并且返回一个包含相同数目元素的新列表。

 Øforeach()方法map()方法类似,但是foreach()方法没有返回值,只用于对参数的结果进行输出。

Øfilter()方法可以移除传入函数的返回值为false的元素。

 Øflatten()方法可以将嵌套的结构展开,即flatten()方法可以将一个二维的列表展开成一个一维的列表。

ØflatMap()方法结合了map()方法和flatten()方法的功能,接收一个可以处理嵌套列表的函数,再对返回结果进行连接。

ØgroupBy()方法可对集合中的元素进行分组操作,返回的结果是一个映射。

 

十二:定义Scala类

        ØScala是一种纯粹的面向对象语言,面向对象语言有两个重要的概念:类和对象

Ø其中,类是对象的抽象,也可以把类理解为模板,对象才是真正的实体

Ø一般定义类的格式如下。

class ClassName(参数列表) extendst {}

Ø一个Scala源文件中可以有多个类,并且Scala类可以有参数。

Ø一般,Scala类名的第一个字母需要大写,如果需要使用几个单词构成一个类的名称,那么每个单词的第一个字母都要大写。

ØJava等其他语言不同的是,Scala中的类不定义为public

ØScala类继承一个类时需要使用关键字extends

ØScala只允许继承一个父类,并且继承父类的所有属性和方法。

Ø子类继承父类中已经实现的方法时,需要使用override关键字,子类继承父类中未实现的方法时,可以不用override关键字。

class Student(val name: String, val age: Int) {
  def this(name: String, age: Int, score: Int) = this(name, age)
  def this(name: String) = this(name, 0)
  def this() = this("Unknown", 0)
  def updateAge(newAge: Int) = this.age = newAge
  def toString = s"$name, $age"
}

object Test {
  def main(args: Array[String]): Unit = {
    val student = new Student("Alice", 20)
    student.updateAge(25)
    println(student.toString)
  }
}

 十三:使用Scala单例模式

ØScala中没有static关键字,因此Scala的类中不存在静态成员。但是Scala可以使用object关键字实现单例模式。

ØScala中使用单例模式时需要使用object定义一个单例对象(object对象),单例对象在整个程序中只有一个实例。单例对象与类的区别在于单例对象不能带参数。

Ø定义单例对象的语法如下。s

scala中实现单例模式的方法

使用object关键字

在Scala中,你可以通过使用object关键字来定义一个单例对象,它会自动继承其所在类的所有成员,包括字段和方法。单例对象在Scala中具有以下特点:

  • 它是该类唯一的实例。

  • 它在第一次被使用时创建,之后每次使用时直接调用。

  • 它可以被赋予一个名字,这个名字可以用来直接调用单例对象的方法。

下面是一个简单的例子:

class MyClass {
  def myMethod = "This is a method in MyClass"
}

object MyClass {
  def myMethod = "This is a method in MyClass companion"
}

在上面的代码中,MyClass类有一个名为myMethod的方法,而MyClass的伴生对象MyClass也有一个同名的方法。你可以通过MyClass.myMethod来调用这个方法,而不需要创建MyClass的实例。

使用伴生对象

在Scala中,一个类可以有一个与之关联的伴生对象,这个伴生对象可以包含静态方法、静态字段等,与Java中的static关键字类似。伴生对象在Scala中具有以下特点:

  • 它与类有相同的名称。
  • 它可以包含静态成员和方法。
  • 它可以在类加载时初始化,也可以延迟初始化。

下面是一个包含伴生对象的例子:

class MyClass {
  def myMethod = "This is a method in MyClass"
}

class MyClassCompanion {
  def myMethod = "This is a method in MyClass companion"
}

object MyClassCompanion {
  def myMethod = "This is a method in MyClass companion"
}

在这个例子中,MyClassCompanionMyClass的伴生类,你可以通过MyClassCompanion.myMethod来调用这个方法。

object ObjectName {}

Ø包含main方法的object对象可以作为程序的入口点。

Ø当单例对象与某个类共享同一个名称时,单例对象被称作这个类的伴生对象,类被称为这个单例对象的伴生类。类和它的伴生对象可以互相访问对方的私有成员。

Ø需要注意的是,必须在同一个源文件里定义类和它的伴生对象。

十四:使用Scala模式匹配

ØScala提供了强大的模式匹配机制。一个模式匹配包含了一系列备选项,每个都开始于关键字case。每个备选项都包含了一个模式及一到多个表达式。模式和表达式之间用=>隔开

ØScala中,使用case关键字定义的类称为样例类。样例类是一种特殊的类,经过优化可应用于模式匹配。

ØScala编译器为样例类添加了一些语法上的便捷设定,具体如下。

在伴生对象中提供了apply()方法,因此不使用new关键字也可以构造对象。

样例类参数列表中的所有参数已隐式获得val关键字。

编译器为样例类添加了toString()hashCode()equals()等方法。

case class Command(command: String)

val userInput = "help"
userInput match {
  case Command("help") => println("Showing help.")
  case Command("exit") => System.exit(0)
  case _ => println("Invalid command.")
}

 十五:读写文件

Ø在实际的实践中,常常需要涉及文件的读写操作

ØScala不提供任何特殊文件写入能力,所以进行文件的写操作使用的是JavaI/O类中的PrintWriter来实现

Ø有时候需要接收用户在屏幕上输入的指令来处理程序,这时可以使用StdIn.readLine接收用户输入的指令。用户可以在REPL中直接输入“StdIn.readLine”命令并按“Enter”键,并在屏幕上输入指令;也可以将StdIn.readLine赋值给一个变量,用户在屏幕上输入的内容即可赋值给该变量。

Ø用户可以使用ScalaSource类读取文件内容。

  • 34
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值