Scala编程语言入门
一. Scala语言基础
1. Scala简介
- Scala是一种多范式的编程语言(支持多种方式的编程),其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于Java平台(Java虚拟机),并兼容现有的Java程序。它也能运行于CLDC配置的Java ME中。目前还有另一.NET平台的实现,不过该版本更新有些滞后。Scala的编译模型(独立编译,动态类加载)与Java和C#一样,所以Scala代码可以调用Java类库(对于.NET实现则可调用.NET类库)。Scala包括编译器和类库,以及BSD许可证发布。
- 使用面向对象编程:封装、继承、多态
- 使用函数式编程:最大的特点
- 优点:代码非常简洁
- 缺点:可读性太差,尤其是隐式类、隐式函数、隐式参数
2. Scala安装与配置
(1) Scala下载
- macOS-2.11.8下载地址:
- downloads.lightbend.com/scala/2.11.…
- 注意:安装时,安装路径中不能含有空格。
(2) 解压后即安装成功
(3) 环境变量配置
- 修改配置文件:
vi /etc/profile
- 增加以下内容:
SCALA_HOME=/Users/apple/Documents/scala-2.11.8
export PATH=$PATH:$SCALA_HOME/bin
- 声明环境变量:
source /etc/profile
(4) 命令行启动
scala
(5) 命令行退出
:quit
3. Scala的IDEA插件配置
(1) 插件下载
(2) 插件安装
- 打开IDEA,点击Configure -> Plugins -> Install plugin from disk,找到本地的Scala插件包安装,完成后重启IDEA
4. Scala代码初体验
(1) 创建Maven
- 创建的maven项目默认是不支持scala的,需要为项目添加scala的framework。
- 在这里选择Scala后,在右边的Use library中配置安装目录即可,最后点击OK。
- 在项目的目录结构中,创建scala文件夹,并标记为source。
(2) 初步测试
- 在scala上点击右键创建scala class,注意选择成Object
-
object FirstDemo { def main(args: Array[String]): Unit = { print("hello world") } } 复制代码
5. Scala的数据类型和变量常量
(1) 数据类型
- 注意:scala中所有的数据都是对象。 e.g.: 1在java中是int,在scala中,1就是一个对象。
- 基本数据类型(Byte,Short,Int,Long,Float,Double):
- Byte: 8位有符号数字,从-128 到 127
- Short: 16位有符号数据,从-32768 到 32767
- Int: 32位有符号数据
- Long: 64位有符号数据
- String: 字符串类型
- Char: 字符
- scala中字符串的插值操作(字符串的拼接)
-
var s1 : String = "Hello " s1: String = "Hello " "My name is Tom and ${s1}" res1: String = My name is Tom and ${s1} 插值操作时,需要加入 s scala> s"My name is Tom and ${s1}" res2: String = "My name is Tom and Hello " 复制代码
-
- Unit类型和Nothing类型
- <1>. Unit类型:
- 即java中的void,没有返回值
-
val f = () f: Unit = () 返回值 Unit类型 () 代表了一个函数,这个函数没有返回值 复制代码
-
- 即java中的void,没有返回值
- <2>. Nothing类型:
- 在执行过程中,产生了异常Exception
- <1>. Unit类型:
- 注意:在Scala中定义变量可以不指定类型,Scala会进行类型的自动推导。
(2) Scala变量的申明和使用
- 使用val申明变量
- e.g.:
scala> val answer = 8 * 3 + 2
- 可以在后续表达式中使用这些名称
- val定义的值实际是一个常量
- e.g.:
- var申明其值可变的变量
- 注意:可以不用显式指定变量的类型,Scala会进行自动的类型推导
6. Scala的函数和方法的使用
(1) scala内置函数(可直接使用)
- e.g.: 求两个数的最大值
- ```
_ 就相当于java中的 * ,代表包内所有内容
import scala.math._
max(1,2)
res4: Int = 2
定义了一个变量 res4 ,接收了 max 函数的返回值。scala中支持类型的推导。
```
复制代码
(2) scala自定义函数
- 语法:
-
def 函数名称([参数名称:参数类型]*) : 返回值类型 = { 函数的实现 } ``` 复制代码
-
- e.g.:
-
<1>. 求和:
-
def sum(x:Int,y:Int):Int = x + y sum: (x: Int, y: Int)Int sum(1,2) res5: Int = 3 ``` 复制代码
-
-
<2>. 求阶乘(5!= 5 * 4* 3 2 1):
-
def myFactor(x:Int):Int = { if(x<=1) 1 else x*myFactor(x-1) } myFactor: (x: Int)Int myFactor(5) res6: Int = 120 复制代码
- 注意:没有return语句,函数的最后一个语句,就是函数的返回值
-
-
<3>. 求输入的年份是否是闰年 - 闰年定义: - 普通闰年:可以被4整除但是不能被100整除的年份 - 世纪闰年:可以被400整除的年份 - ``` scala> def isLeapYear(x:Int) = { if(( x%4 == 0 && x%100 != 0) || (x%400==0)) true else false } isLeapYear: (x: Int)Boolean
isLeapYear(2019) res7: Boolean = false isLeapYear(2008) res8: Boolean = true ``` 复制代码
- 注意:函数定义的时候,可以不写返回值,因为scala支持类型推导
-
7. Scala的条件表达式
- Scala的if/else语法结构和Java或C++一样。
- Scala中,if/else是表达式,有值,这个值就是跟在if或else之后的表达式的值。
8. Scala的循环
- Scala拥有与Java和C++相同的while和do while循环
- Scala中,可以使用for和foreach进行迭代
- 使用for循环案例:
-
import util.control.Breaks._ import scala.math._ /** * * @ClassName: Demo1 * @Description * @Author: YBCarry * @Date2019-05-07 08:15 * @Version: V1.0 * **/ object Demo1 { def main(args: Array[String]): Unit = { /* * for 循环 * 定义一个集合 * */ var list = List("Mary", "Tom", "Mike") println("--------for循环的第一种写法--------------") /* * <- 表示Scala中的generator,即:提取符 把list中的每一个元素,赋给s * */ for (s <- list) println(s + " ") println("--------for循环的第二种写法--------------") //打印长度大于3的名字 加判断 for { s <- list if (s.length > 3) } println(s) println("--------for循环的第三种写法--------------") //对第二种,进一步简化 for (s <- list if s.length <= 3) println(s) println("--------for循环的第四种写法--------------") /* * 1、把list中所有元素,都变成大写 * 2、返回一个新的集合 * yield 记录每次迭代中的有关值,并逐一存入到新数组 * */ var newList = for { s <- list s1 = s.toUpperCase() } yield (s1) for (s <- newList) println(s) println("--------while循环--------------") //定义循环变量 var i = 0 while (i < list.length) { println(list(i)) /* * 自增 * 注意: scala中 没有 i++ * */ i += 1 } println("--------do while循环--------------") //定义一个循环变量 var j = 0 do { println(list(j)) j += 1 } while (j < list.length) println("--------for each 循环--------------") /* * foreach(没有返回值) scala里面有 spark里面会用到 * map循环(有返回值) * * foreach 说明 * foreach 是list的一个方法 * 把一个函数,传入了foreach —> 高阶函数(函数式编程) * */ list.foreach(println) /** 循环应用实例 */ /* * 1. 判断101-200之间有多少个素数 * * 判定素数的方法: * x % 2 -- x % sqrt(根号) x * 当都不能被整除的时候,就是素数 * * e.g.: * 16 sqrt(16)=4 * 故循环除2 3 4 * 验证能否被整除:16%2 == 0 ? * * 编程思路 —— 两层循环: * 第一层 101-200 * 第二层 2 sqrt第一层 * * */ println("-----------1. 判断101-200之间有多少个素数-------------") println("-----------循环嵌套-------------") var count: Int = 0 //保存结果 var index_outer = 0 //外层循环变量 var index_inner = 0 //内层循环变量 for (index_outer <- 101 until 200) { var b = false //标识是否能被整除 index_inner = 2 /* * scala中使用的是函数块的风格来解决break和continue的问题 * 相比java的一个关键词搞定的写法有点复杂 * 但符合函数式编程的风格 * */ breakable { while (index_inner <= sqrt(index_outer)) { if (index_outer % index_inner == 0) { b = true break } index_inner += 1 } } if (!b) count += 1 } println("素数个数为:" + count) /* *1. 冒泡排序 * * 算法分析: * 1、比较相邻的元素。如果第一个比第二个大,就交换 * 2、对每一对相邻元素都做上述工作,循环完第一次后,最后的元素,就是最大的元素 * 3、针对剩下的元素,重复上面工作(除了最后一个元素) * * 程序分析: * 1、两层循环 * 2、外层循环控制比较的次数 * 3、内层循环控制到达的位置,就是 结束比较 的位置 * */ println("-----------2. 冒泡排序-------------") var a = Array(12, 3, 6, 2, 4, 7, 9, 5, 30, 1) //初始数据 println("-----------排序前-------------") a.foreach(println) for (i <- 0 until a.length - 1) { for (j <- 0 until a.length - i - 1) { if (a(j) > a(j + 1)) { // 判断相邻元素大小 var tmp = a(j) a(j) = a(j + 1) a(j + 1) = tmp } } } println("-----------排序后-------------") a.foreach(println) } } 复制代码
-
9. Scala函数的参数
(1) 函数参数的求值策略
- <1>. Call By Value:
- 对函数实参求值,且仅求一次
- <2>. Call By Name:
- 函数实参每次在函数体内被用到时都会求值
- e.g.1:
-
scala> def test1(x:Int,y:Int) = x + x test1: (x: Int, y: Int)Int scala> test1(3+4,8) res9: Int = 14 scala> def test2(x : => Int,y : => Int) = x+x test2: (x: => Int, y: => Int)Int scala> test2(3+4,8) res10: Int = 14 执行过程对比: test1 ---> test1(3+4,8) ---> test1(7,8) ---> 7+7 ---> 14 test2 ---> test2(3+4,8) ---> (3+4) + (3+4) ---> 14 复制代码
-
- e.g.2:
-
def bar(x:Int,y : => Int) : Int = 1 x 是 value y 是 name 定义一个死循环: def loop() : Int = loop 调用bar函数的时候: 1、bar(1,loop) 2、bar(loop,1) 问题:哪个方式会产生死循环? scala> def bar(x:Int,y : => Int) : Int = 1 bar: (x: Int, y: => Int)Int scala> def loop() : Int = loop loop: ()Int scala> bar(1,loop) res11: Int = 1 scala> bar(loop,1) 解析: 1、虽然 y 是 name, 每次调用的时候会被求值。但是,函数体内,没有调用到y。 2、x 是 value,对函数参数求值,并且只求一次。虽然后面没有用到x,但求值时产生了死循环。 复制代码
-
(2) Scala中的函数参数
- <1>. 默认参数
- 当没有给参数值赋值的时候,就会使用默认值。
- <2>. 代名参数
- 当有多个默认参数的时候,通过代名参数确定给哪个函数参数赋值。
- <3>. 可变参数
- 类似于java中的可变参数,即参数数量不固定。
10. Scala的Lazy值(懒值)
- 当val被申明为lazy时,它的初始化将被推迟,直到我们首次对它取值。
11. 异常的处理
- Scala异常的工作机制和Java或者C++一样。直接使用throw关键字抛出异常。
12. Scala中的数组
(1) Scala数组的类型:
- <1>. 定长数组:
- 使用关键字Array
-
val a = new Array[Int](10) (10) 就是数组的长度 初始化赋给默认值 Array[String] = Array("Tom","Lily") 复制代码
- 注意:不能往数组中添加不同类型的元素
- <2>. 变长数组:
- 使用关键字ArrayBuffer
-
import scala.collection.mutable._ val b = ArrayBuffer[Int]() 增加元素: b += 1 b.type = ArrayBuffer(1) b += 2 b.type = ArrayBuffer(1, 2) b += (1,2,3,4) b.type = ArrayBuffer(1, 2, 1, 2, 3, 4) 删除最后两个元素: b.trimEnd(2) scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 1, 2) 遍历数组: for:for(s <- a ) println(s) foreach:a.foreach(println) 最值: b.max b.min 排序: 降序:b.sortWith(_>_) 升序:b.sortWith(_<_) 复制代码
(2) 多维数组
- 通过数组的数组来实现
-
创建X行Y列的数组 var c = Array.ofDim[Int](X,Y) 为x行y列赋值为n c(x - 1)(y - 1) = n 定义一个二维数组,其中每个元素是一个一维数组,并且长度不固定 var d = new Array[Array[Int]](10) for(i <- 0 until d.length){ d(i) = new Array[Int](i+1) } 二维数组,如果使用 Array[Array[Int]](10) 声明时: 1、首先指定的是外层数据的长度 2、初始化内层数组的时候,再指定内层数组的长度 复制代码
13. 映射
- 映射就是Map集合,由一个(key,value)对组成。
-
val scores = Map(“Alice” -> 10,”Bob” -> 3,”Cindy” -> 8) 复制代码
- 映射的类型
- 可变Map:scala.collection.mutable
- 可变Map:scala.collection.immutable