瞬息 电光石火、转瞬即逝

scala 相关

2022-02-24
偷老师的


上课用的,直接偷来丢这里方便点

Scala

1.学习目标

2. Scala介绍

2.1 简介

img

​ Scala 语言的创始人 Martin Odersky(马丁·奥德斯基)

再回到我们的Scala语言,在Scala官网 https://www.scala-lang.org/ 介绍了其六大特征。

  1. java 和 scala 可以混编

  2. 类型推测(自动推测类型)

  3. 并发和分布式(Actor)

  4. 特质,特征(类似 java 中 interfacesabstract 结合)

  5. 模式匹配(类似 java 中的 switch…case

  6. 高阶函数

一句话总结:Scala 是一门以 jvm 为运行环境的静态类型编程语言,具备面向对象及函数式编程的特性。

### 2.2 具体应用

kafka : 分布式消息队列,内部代码经常用来处理并发的问题,用 scala 可以大大简化其代码。

Spark : 方便处理多线程场景,另外 Spark 主要用作内存计算,经常要用来实现复杂的算法,利用 scala 这种函数式编程语言可以大大简化代码。

3. Scala安装使用

3.1 安装步骤

(1)首先确保 JDK1.8 安装成功

(2)下载对应的 Scala 安装文件 scala-2.12.11.zip

​ 版本选择:

image-20220224194230340

根据我们接下来学习的spark的版本来决定Scala的版本,具体选择情况在spark的官网进行查看。

https://spark.apache.org/docs/ 到里面找对应spark版本的文档进行查看

(3)解压 scala-2.12.11.zip,我这里解压到 D:\Tools

(4)配置 Scala 的环境变量

img

image-20220224194413654

注意 1:解压路径不能有任何中文路径,最好不要有空格。

注意 2:环境变量要大写 SCALA_HOME

2)测试

需求:计算两数 a 和 b 的和。

步骤:

(1)在键盘上同时按 win+r 键,并在运行窗口输入 cmd 命令

img

(2)输入 Scala 并按回车键,启动 Scala 环境。然后定义两个变量,并计算求和。

img

3.2 Scala插件安装

默认情况下 IDEA 不支持 Scala 的开发,需要安装 Scala 插件。

1)插件离线安装步骤

( 1 )建议将该插件 scala-intellij-bin-2020.3.12.zip 文件,放到 Scala 的安装目录 D:\Tools\scala-2.12.11 下,方便管理。

(2)打开 IDEA,在左上角找到 File->在下拉菜单中点击 Setting… ->点击 Plugins->点击右 上 角齿轮 Install plugin from disk… , 找 到 插 件 存 储 路 径D:\Tools\scala-2.12.11\scala-intellij-bin-2020.3.12.zip,最后点击 ok。

image-20220224193912881

2)插件在线安装(可选)

(1)在搜索插件框里面输入 Scala->点击 Install->点击 ok->点击 apply。

image-20220224193925941

(2)重启 IDEA,再次来到 Scala 插件页面,已经变成 Uninstall。

image-20220224193937006

### 3.3 HelloWorld案例

1)打开 IDEA->点击左侧的 Flie->选择 New->选择 Project…

image-20220224194007690

2)创建一个 Maven 工程,并点击 next

image-20220224194014539

3)GroupId 中输入 org.example -> ArtifactId 中输入 scala01 -> 点击 next -> 点击 Finish

image-20220224194022831

注意:工程存储路径一定不要有中文和空格。

4)默认下,Maven 不支持 Scala 的开发,需要引入 Scala 框架。

在 scala01 项目上,点击右键-> Add Framework Support… ->选择 Scala->点击 OK

image-20220224194030384

注意:如果是第一次引入框架,Use libary 看不到,需要选择你的 Scala 安装目录,然后工具就会自动识别,就会显示 user libary。

5)创建项目的源文件目录

右键点击 main 目录->New->点击 Diretory -> 写个名字(比如 scala)。

右键点击 scala 目录->Mark Directory as->选择 Sources root,观察文件夹颜色发生变化。

image-20220224194038657

6)在 scala 包下,创建包 com.bigdata.chapter01 包名和 Hello 类名,

右键点击 scala 目录->New->Package->输入 com.bigdata.chapter01->点击 OK。

右键点击 com.bigdata.chapter01->New->Scala Class->选择 Object->Name 项输入HelloWorld。

image-20220224194047139

8)编写输出 Hello Scala 案例

在类中中输入 main,然后回车可以快速生成 main 方法;

在 main 方法中输入 println(“hello scala”)

image-20220224194053710

说明:Java 中部分代码也是可以在 Scala 中运行。

4. Scala基础

4.1 数据类型

Scala 与 Java 有着相同的数据类型,在 Scala 中数据类型都是对象,也就是说 scala 没有 java 中的原生类型。

Scala数据类型分为两大类 AnyVal(值类型) 和 AnyRef(引用类型), 注意:不管是 AnyVal 还是 AnyRef 都是对象。

image-20220224193759222

image-20220224193810576

比较特殊的 None,它是 Option 的两个子类之一,另一个是 Some,用于安全的函数返回值

scala 推荐在可能返回空的方法使用 Option[X] 作为返回类型,如果有值就返回 Some[X],否则返回 None

def get(key : A): Option[B] ={
	if (contains(key))
		Some(getValue(key))
	else
		None
}

Nil 表示长度为0的 List

4.2 变量和常量的声明

​ 注意如下两点:

  1. 定义变量或者常量的时候,也可以写上返回的类型,一般省略,如:val a :Int = 10

  2. 常量不可再赋值

/**
* 定义变量和常量
* 变量:用 var 定义,可修改
* 常量:用 val 定义,不可修改
*/
var name ="zhangsan"
println(name)
name = "lisi"
println(name)
val gender = "m" 
//  gender ="d" // 错误,不能给常量再赋值

4.3 类与对象

4.3.1 创建类

class Person() {
    val name = "zhangsan"
    val age =18
    def sayName() = {
      "my name is " + name
    }
}

4.3.2 创建对象

object Lesson_class {
  def main(args: Array[String]): Unit = {
    val person = new Person
    println(person.name)
    println(person.sayName())
  }
}

4.3.3 伴生类与伴生对象

class Student(xname :String,xage :Int){
  var name = Student.name
  var age = xage
  var gender = "m"
  def this(name:String,age:Int,g:String){
    this(name,age)
    gender = g
  }
  def sayName() = {
    "my name is " + name
  }
}
object Student {
  val name ="张三丰"
  def main(args: Array[String]): Unit = {
    val student = new Student("wangwu",10,"f")
    println(student.age)
    println(student.sayName())
    println(student.gender)
  }
}

注意点:

  • 建议类名首字母大写,方法首字母小写,类和方法命名建议符合驼峰命名法

  • scala中object是单例对象,相当于java中的工具类,可以看成是定义静态的方法的类,object不可以传参数,另:Trait不可以传参

  • scala中的class类默认可以传参数,默认的传参数就是默认的构造函数,重写构造函数的时候,必须要调用默认的构造函数

  • class 默认实现了getter setter方法

  • 使用object时,不用new,使用class时要new,并且new的时候,class中除了方法不执行,其他的都执行

  • 如果在同一个文件中,object对象和class类的名称相同,则这个对象就是这个类的伴生对象,这个类就是这个对象的伴生类,可以互相访问私有变量

    上述这些注意点就是scala中oop面向对象的注意点

4.3.4 if else

同 java 一样的语法结构。有:单,双,多分支这样几种结构

/**
 * StdIn键盘标准输入 scala.io包下的
 */
def main(args: Array[String]): Unit = {
  val age = StdIn.readInt()
  if (age >= 18 && age < 100) {
    println("成年")
  } else if (age > 0 && age < 18)
    println("未成年")
  else {
    println("????")
  }
}

4.3.5 for,while,do…while

  1. to 和 until 的用法(不带步长,带步长的区别)

    /**
     * to和until 
     * 1 to 10 返回1到10的Range数组,包含10
     * 1 until 10 返回1到10的Range数组,不包含10
     */
    println(1 to 10) //打印1,2,3,4,5,6,7,8,9,10
    println(1 to (10))
       
    println(1 to (10,2)) //步长为2,从1开始打印 1,3,5,7,9
    println(1.to(10,2)) //与上面等价
       
    println(1 until 10) //不包含最后一个数,1,2,3,4,5,6,7,8,9
    println(1 until(10))//与上面等价
    print(1 until(10,3)) //步长为3,从1开始打印,1,4,7
    
  2. 创建for循环

    /**
     * for 循环
     */
    for(i <- 1 to 10) {
      println(i)
    }
    
  3. 创建多层for循环

    //可以分号隔开,构成多层for循环
    //scala中 不能写count++ count-- 只能写count+
    var count = 0;
    for(i <- 1 to 10; j <- 1 until 10){
      println("i="+ i +", j="+j)
      count += 1
    }
    println(count)
    
image-20220224133002266
//例子: 打印九九乘法表
for(i <- 1 to 9 ; j <- 1 to i){
    print(j + "*" + i + "=" + (i * j) + "\t")
    if(j == i){
        println()
    }
}
  1. for 循环中可以加条件判断,分号隔开

    //可以在for循环中加入条件判断
    for(i<- 1 to 10 ;if (i%2) == 0 ;if (i == 4) ){
      println(i)
    }
    
  2. scala中不能使用 count++ , count– 只能使用 count = count+1 , count += 1

  3. for循环用yield 关键字返回一个集合( for {子句} yield {变量或表达式} ,原来的集合不会被改变,只会通过你的for/yield构建出一个新的集合。)

  4. while循环(两种方式), while(){}do {}while()

    //将for中的符合条件的元素通过yield关键字返回成一个集合
    val list = for(i <- 1 to 10 ; if(i > 5 )) yield i
    for( w <- list ){
      println(w) 
    }
    /**
     * while 循环
     */
    var index = 0
    while(index < 10 ){
      println("第"+index+"次while 循环")
      index += 1 }
       
      index = 0
    do{
      index +=1
      println("第"+index+"次do while 循环") 
    }
    while(index <10 )
    

5 .Scala 函数

5.1 Scala函数的定义

有参函数

无参函数

image-20220224100442812

def fun(a: Int, b: Int): Unit = {
  println(a + b)
}
fun(1, 1)

def fun1(a: Int, b: Int) = a + b
println(fun1(1, 2))

注意点:

  1. Scala 使用 def 关键字告诉编译器这是一个方法。

  2. 我们可以通过在参数后面加一个冒号和类型来显式地指定返回类型。

  3. 方法可以写返回值的类型也可以不写,会自动推断,有时候不能省略,必须写,比如在递归函数中或者函数的返回值是函数类型的时候。

  4. Scala 中函数有返回值时,可以写 return ,也可以不写 return ,不写 return 时会把函数中最后一行当做结果返回。当写 return 时,必须要写函数的返回类型。如果返回值可以一行搞定,可以将 {} 省略不写

  5. 传递给方法的参数可以在方法中使用,并且 scala 规定方法的传过来的参数为 val 的,不是 var的。

  6. 如果去掉方法体前面的等号,那么这个方法返回类型必定是 Unit 的。这种说法无论方法体里面什么逻辑都成立, scala 可以把任意类型转换为 Unit 。假设,函数里面的逻辑最后返回了一个string ,那么这个返回值会被转换成 Unit ,原本逻辑的值会被丢弃。

5. 2 递归函数

/**
 * 递归函数
 * 5的阶乘
 */
def fun2(num :Int) :Int ={
  if(num ==1){
    num
  }else{
    num * fun2(num-1)
  }
}
print(fun2(5))

5.3 包含参数默认值的函数

  • 默认值的函数中,如果传入的参数个数与函数定义相同,则传入的数值会覆盖默认值。
  • 如果不想覆盖默认值,传入的参数个数小于定义的函数的参数,则需要指定参数名称。
   /**
    * 包含默认参数值的函数
 * 注意:
 * 1.默认值的函数中,如果传入的参数个数与函数定义相同,则传入的数值会覆盖默认值
 * 2.如果不想覆盖默认值,传入的参数个数小于定义的函数的参数,则需要指定参数名称
 */
   def fun3(a:Int=10,b:Int) : Unit ={
     println(a+b)
   }
   fun3(b=2)

5.4 可变参数个数的函数

​ 可变参数理解为不定长数组,传递多个参数时用逗号分开,处理起来按照数组方式。

/**
 * 可变参数个数的函数
 * 注意:多个参数逗号分开
 */
def fun4(elements: Int*) = {
    var sum = 0;
    for (elem <- elements) {
    sum += elem
  }   
    sum
}
println(fun4(1, 2, 3, 4))

5.5 匿名函数

一共有如下三种:

  1. 有参匿名函数
  2. 无参匿名函数
  3. 有返回值的匿名函数
    • 可以将匿名函数返回给val定义的值
    • 匿名函数不能显式声明函数的返回类型
/**
 * 匿名函数  (参数)=>{方法体}
 * 1.有参数匿名函数
 * 2.无参数匿名函数
 * 3.有返回值的匿名函数
 * 注意:
 * 可以将匿名函数返回给定义的一个变量
 */
//有参数匿名函数
val value1 = (a : Int) => {
   println(a) }
value1(1)
//无参数匿名函数
val value2 = ()=>{
   println("我爱中国") }
value2()
//有返回值的匿名函数
val value3 = (a:Int,b:Int) =>{
   a+b }
println(value3(4,4))

5.6 嵌套函数

/**
 * 嵌套函数
 * 例如:嵌套函数求5的阶乘
 */
def fun5(num:Int)={
   def fun6(a:Int,b:Int):Int={
       if(a == 1){
            b
        }else{
            fun6(a-1,a*b)
        }
  }
   fun6(num,1) 
}
println(fun5(5))

5.7 偏应用函数

​ 偏应用函数是一种表达式,不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数。如下场景中,一个参数是完全相同,另一个参数不同,在这样一个情况之下,我们可以使用偏应用函数来进行优化。

/**
 * 偏应用函数
 */
def log(date :Date, s :String)= {
   println("date is "+ date +",log is "+ s) }
val date = new Date()
log(date ,"log1")
log(date ,"log2")
log(date ,"log3")
//想要调用log,以上变化的是第二个参数,可以用偏应用函数处理
val logWithDate = log(date,_:String)
logWithDate("log11")
logWithDate("log22")
logWithDate("log33")

5.8 高阶函数

​ 函数的参数是函数,或者函数的返回类型是函数,或者函数的参数和函数的返回类型是函数的函数。

  1. 函数的参数是函数

  2. 函数的返回是函数

  3. 函数的参数和函数的返回是函数

/**
 * 高阶函数
 * 函数的参数是函数 或者函数的返回是函数 或者函数的参数和返回都是函数
 */   
//函数的参数是函数
def hightFun(f : (Int,Int) =>Int, a:Int ) : Int = {
   f(a,100) }
def f(v1 :Int,v2: Int):Int  = {
   v1+v2
}
 
println(hightFun(f, 1))
   
//函数的返回是函数
//1,2,3,4相加
def hightFun2(a : Int,b:Int) : (Int,Int)=>Int = {
   def f2 (v1: Int,v2:Int) :Int = {
       v1+v2+a+b
  }
   f2
}
println(hightFun2(1,2)(3,4))
 
//函数的参数是函数,函数的返回是函数
def hightFun3(f : (Int ,Int) => Int) : (Int,Int) => Int = {
   f
}
println(hightFun3(f)(100,200))
println(hightFun3((a,b) =>{a+b})(200,200))
//以上这句话还可以写成这样
//如果函数的参数在方法体中只使用了一次 那么可以写成_表示
println(hightFun3(_+_)(200,200))

5.9 柯里化函数

​ 柯里化–颗粒化,将参数变成颗粒散落简而言之就是将参数拆拆拆。函数柯里化基本是在做这么一件事情:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。如果写成公式文字就是这样。

fn(a,b,c,d)=>fn(a)(b)(c)(d);
fn(a,b,c,d)=>fn(a,b)(c)(d);
fn(a,b,c,d)=>fn(a)(b,c,d);

可以理解为高阶函数的简化。和我们文档中上面的函数的返回类是函数的例子进行比较。

//def fun7(a :Int,b:Int,c:Int,d:Int) = {
// a+b+c+d
//}
//println(fun7(1,2,3,4))
/**
 * 柯里化函数
 */
def fun7(a :Int,b:Int)(c:Int,d:Int) = {
   a+b+c+d }
println(fun7(1,2)(3,4))//执行完1+2得出3之后继续往下作为函数执行3+3+4最终求出结果10

前一篇 点点点

评论

折跃门