Scala学习笔记-方便查找使用 为Spark学习打基础

Scala学习

注意

本文适合有C、C++、Java等编程语言基础的人查看。

伊始

为什么是Scala

毕业设计所逼,我课题是《基于spark的实时音乐推荐系统》,而Spark是用Scala开发的,使用Scala必然最合适。虽然Spark提供了Java、Python等的API,但貌似效果不如Scala。
且Scala自身较为独特,和Java还有很深的渊源,毕竟可以说Scala和Java8.0都出自一人之手。

网课

自己找了个网课,开干。

环境

电脑环境安装

xxx

关于版本的声明

注意,不同的Scala版本,可能会有不同的运行结果。选择版本时,要兼顾Spark等其它框架、软件的要求

我的环境和版本

后文所有内容都将基于以下版本展开,最新情况请参考官方文档

  • java version “1.8.0_201”
  • Scala code runner version 2.11.8
  • IntelliJ IDEA 2018.2.4 (Ultimate Edition)
    构建 #IU-182.4505.22, 建于 September 18, 2018
    Subscription is active until January 1, 2100
    JRE: 1.8.0_152-release-1248-b8 amd64
    JVM: OpenJDK 64-Bit Server VM JetBrains s.r.o
  • Windows 10 10.0

在这里插入图片描述

补充:于2021年4月2日将IDEA切换为IntelliJ IDEA 2020.3.3 (Ultimate Edition)
补充:于2021年4月5日将Scala由2.11.8切换为2.13.5

IDEA插件安装

若在idea内无法下载,需要前往idea官网下载后再安装,注意插件版本问题!!! 插件需要根据idea版本选择

IDEA关联Scala源码

xxxx

基础部分1

文档注释和生成

注释
对于方法(函数)的注释,写好方法后,在其上一行键入/**并按回车,其注释模板会自动补全:

/**
  * @example
  * 输入10和20 输出30
  * @param number1 数字1
  * @param number2 数字2
  * @return 两数之和
  */
def plus(number1: Int, number2: Int): Int = {
   
	return number1 + number2;
}

文档生成
在powershell中(保证路径无误):

scaladoc.bat -d d:\scala\mydoc\ TestScala.scala

在这里插入图片描述

在这里插入图片描述

字符串的三种输出

var name = "A_Nan"
var age = 20
//		三种输出方式
println("1. hello" + name + age)
printf("2. hello name: %s\tage: %d\n", name, age)
println(s"3. hello name: $name\tage: ${age + 20}")

结果:
在这里插入图片描述

变量和常量

var生成变量

var age: Int = 10
var age2 = 20 //可省略数据类型 自动推导
var num: Double = 10.9
var name: String = "hello"
var isPass: Boolean = true
var score: Float = 11.8f

val生成常量,不可再次修改,该种生成方法效率更高(因为在底层不用考虑线程安全问题)
Scala设计者推荐使用val
例如:一般生成对象后就不再重新指定对象,因此生成对象时可使用val

val Dog = new Dog //生成val对象 提高效率
Dog.name = "hello" //对象的属性可正常修改
Dog.name = "123" 

class Dog {
   
	var name: String = ""
}

数据类型

  • Scala与Java有着相同的数据类型,但Scala中数据类型都是对象,即:Scala中没有Java的原生类型
  • Scala数据类型分为值类型(AnyVal)和引用类型(AnyRef),这两者都是对象

这样就有一个好处——变量有大量方法可以使用:
在这里插入图片描述

注:无参方法可省略括号

var age: Int = 10
age.toString()
age.toString//无参方法可省略括号

数据类型体系图

在这里插入图片描述

小结
  • 在Scala中有一个根类型Any,它是所有类的父类
  • 在Scala中一切皆为对象,分两大类——值类型引用类型,均为Any子类
  • Null类型时Scala的特别类型,只有一个null值,它是bottom class,是所有引用类型的子类
  • Nothing类型也是bottom class,它是所有类子类,在开发中可将Nothing类型的值返回给任意变量和函数,在抛出异常中使用很多
  • 在Scala中存在隐式转换(implicit conversion):低精度的值向高精度的值自动转换

具体数据类型

在这里插入图片描述

整形
var i = 10  //Int
var j = 10l //Long
var k = 10L //Long

注意:
在这里插入图片描述
这里报错的直接原因不是数字大小超过了变量number的范围,而是因为默认情况下数字均是int类型,错误发生在赋值号之后,将数字改为Long即可:
在这里插入图片描述

Long最大值和最小值:

println("MinLong:" + Long.MinValue)
println("MaxLong: " + Long.MaxValue)

在这里插入图片描述

处理大数据会有专用类型,Long的范围远远不够!

浮点型
  • 默认为Double,声明为Float要在数字后加“f”或“F”。
    在这里插入图片描述

  • 浮点型常量表示形式(两种):
    十进制形式:5.12, 512.0f,. 512(必须要有小数点)
    科学计数法:5.12e2, 5.12E-2

字符型(char)
  • 字符常量是用单引号括起来的单个字符。
    例如:var ch = 'a'

  • 也允许使用转义字符
    例如:var ch = '\n' //表示换行符

  • 可以直接给char变量赋予整数,会按照Unicode编码保存

  • char可以进行运算

    var ch: Char = 'a'
    var number: Int = 10 + ch + 'b'
    println("number: " + number)
    

    结果:
    在这里插入图片描述

以下为错误赋值:
在这里插入图片描述

Unit,Null,Nothing

在这里插入图片描述

自动数据类型转换

在这里插入图片描述

高级隐式转换和隐式函数

Scala提供了非常强大的隐式转换机制(隐式函数,隐式类等),请看高级部分

强制类型转换

将容量大的数据类型转为容量小的数据类型,但可能会造成精度降低或溢出。

var num: Int = 2.7.toInt	//使用强转方法
细节说明

在这里插入图片描述

值类型和String类型的转换

基本数据类型转String
var str: String = 123 + ""
String转基本数据类型

使用String的.toXxx方法

var str: String = "1234"
var num: Int = str.toInt
var num_double: Double = str.toDouble

注意:

转换时确保能够将String转为有效数据,例如:

  • 不能将"hello"转为整数
  • 不能将"12.2"转为Int

标识符

标识符概念

  • Scala对各种变量、方法、函数等命名时使用的字符序列称为标识符
  • 凡是自己可以起名字的地方都叫标识符

标识符的命名规则

  • 首字符为字母,后续字符任意字母和数字,美元符号,可后接下划线_
  • 数字不可以开头。
  • 首字符为操作符(比如+ - * /),后续字符也需跟操作符,至少一个(反编译)
  • 操作符(比如+ - * /)不能在标识符中间和最后.
  • 用反引号``包括的任意字符串,即使是关键字(39个)也可以,如
     var `true` = 123
    

运算符

/

var num1: Int = 10 / 3
var num2: Double = 10 / 3
var num3: Double = 10.0 / 3
println("num1: " + num1)
println("num2: " + num2)
println("num3: " + num3)
println("num3: " + num3.formatted("%.2f"))

在这里插入图片描述

%

在这里插入图片描述

自增自减

var num = 1
num += 1 //代替++
num -= 1 //代替--

关系运算符

生成的值均为Boolean(true或false)
规则同C、Java,此处不做介绍

逻辑运算符

  • &&
  • ||
  • !

赋值运算符

在这里插入图片描述
在这里插入图片描述

位运算符

在这里插入图片描述

运算符优先级

在这里插入图片描述

基础部分2

读取控制台输入

package com.test.scala01

import scala.io.StdIn

object Test2 {
   
	def main(args: Array[String]): Unit = {
   
		val name: String = StdIn.readLine()
		println(name)
	}
}

流程控制

顺序

整体类似于Java,从前到后、从上到下执行
变量是向前引用,但对象、方法无限制

分支

单分支
val num = 12
if(num > 10) {
   
	print("yes")
}
双分支
val num = 122
if (num > 1000) {
   
	print("yes")
} else {
   
	print("no")
}
多分支
val num = 122
if (num == 100) {
   
	print("100")
} else if (num == 111) {
   
	print("111")
} else {
   
	print("no")
}
分支嵌套

同Java、C等语言,此处不做介绍,推荐嵌套不要超过三层

Scala中没有switch

在Scala中用模式匹配来处理,该方法需要知识较多,后文进行介绍

注意

if else中:

  • 若大括号内只有一行代码,那么大括号可以省略

  • Scala中任何表达式都有返回值,包括if else表达式,if else的返回内容取决于满足条件的代码体的最后一行内容,以下为特殊情况:

    val num = if (12 == 100) {
         
    	200
    }
    print(num)
    

    在这里插入图片描述
    这里小括号()表示返回值为Unit,即

  • Scala中没有三元运算符,因为可以用if else代替:

    val num = if (12 > 100) 12 else 100
    
    

循环

for

第一种:

for (i <- 1 to 5) {
       //[1, 5]
	println(i)  //一共循环五次
}

结果:
在这里插入图片描述
第二种:

for (i <- 1 until 5) {
     //[1,5)
	println(i)  //循环4次
}

for循环中引入变量:
可以在原括号内的后面加上分号;后再创建新的变量,让编程更灵活

for (i <- 1 until 5; j = 6 - i) {
     //分号不可省略
	println(j)
}

运行结果:
在这里插入图片描述
注意,在Scala的for循环中,分号;的意义和C、Java等语言中for循环的分号不一样了,以至于Scala的for循环嵌套可以这么写:

for (i <- 1 to 3; j <- 1 until 3) {
   
	println("i=" + i + "  j=" + j)
}

运行结果如下:
在这里插入图片描述
这种写法相当于:

for (i <- 1 to 3) {
   
	for (j <- 1 until 3) {
   
		println("i=" + i + "  j=" + j)
	}
}

在实际的使用中,还是这种原始的方法更为灵活。说实话,把两个循环条件写一个括号里没求用,花里胡哨反而影响代码阅读。。。。。。

注意1:

  • {}()对于for来说都可以

    for (i <- 1 to 5) {
           //两个for循环功能相同
    	print(i)
    }
    for {
         i <- 1 to 5} {
           //两个for循环功能相同
    	print(i)
    }
    
  • 不成文规定(非强制性):for推导式仅包含单一表达式用小括号,多个表达式用大括号

  • 当使用大括号{}换行写表达式时,分号可省略

    for {
         
    	i <- 1 to 10 
    	j = 2 * i
    } {
         
    	println(j)
    }
    

注意2:
要想理解1 to 10,不妨先想想以下代码可否执行?结果是怎样?

print(1 to 10)

运行结果:
在这里插入图片描述
有意思吧,Range()这个东西用处多多,下面还会提到。

注意3:
for循环步长的控制

  • 使用Range()
for (i <- Range(1, 10, 2)) {
    //范围[1, 10),步长为2
	println(i)
}

在这里插入图片描述

  • 使用循环守卫(见下文)
reverse的作用

reverse查一下单词意思就明白了

reverse适用于List

val list = List(1, 2, 3)
println(list)//1, 2, 3
println(list.reverse)//3, 2, 1

因此,如果要反着循环,可以通过如下实现:

  • 使用reverse

    for (i <- 0 to 10 reverse) {
         
    	println(i)
    }
    

    当然,前文提到了0 to 10就是Range(0, 11),因此也可以这么写:

    for (i <- (0 to 10).reverse) {
         
    	println(i)
    }
    

    或:

    for (i <- Range(0, 11).reverse) {
         
    	println(i)
    }
    
  • 当然,通过减法运算也可实现一样的效果

    for (i <- 0 to 10) {
         
    	println(10 - i)
    }
    
for循环返回值

如同if语句一样,for循环语句也有返回值,但要借助yield

val num = for (i <- 1 to 3) yield i;
print(num)

结果如下:
在这里插入图片描述
这就表明,yield返回的对象是Vector,也就是会把每次循环得到的变量i放入Vector中,循环结束后将Vector赋值给变量num。同时,在yield后面可以是一个代码块,这样就会有更强的灵活性:

val num = for (i <- 1 to 3) yield {
   
if (i * i > 2 * i) {
   
		i * i
	} else {
   
		2 * i
	}
};
print(num)

结果如下:
在这里插入图片描述
这种特性,正体现了Scala中的一个重要特点,就是Scala擅长将一个集合中的每一个元素都进行处理后,返回给新的集合。

for循环守卫

循环守卫也叫循环保护式、条件判断式、守卫。保护式true则进入循环体,为false则跳过,类似C语言中的continue
例如:

for (i <- 1 until 5 if i != 2) {
   
	println(i)
}

该代码一共会循环四次,其中i等于2时跳过
故也可以通过此方法来实现循环步长的控制:

for (i <- 1 to 10 if i % 2 != 0) {
   
	println(i)
}

在这里插入图片描述

while循环

同C、Java等语言相似:

  • 循环条件是返回一个布尔值的表达式
  • while循环是先判断再执行语句
  • if语句不同,while语句本身没有值,即整个while语句的结果是Unit类型的,即()
  • 因为没有返回值,所以当要用该语句来计算并返回结果时,就需要使用外部变量,即在while循环的外部声明变量,所以会造成循环内部对外部的变量造成了影响,而这是与Scala的设计理念相违背的,因此更推荐使用for循环

例子如下:

var i = 0
while (i < 10) {
   
	i += 1
	print("hello:" + i)
}
do-while循环

同样不推荐使用,理由同上
例子:

var i = 0
do {
   
	i += 1
	println(i + " hello")
} while (i < 5)

循环中断

在Scala中是没有breakcontinue这两个关键字的,所以无法像C、Java等语言一样通过它们来实现循环控制。这么做是为了更好地适应函数化编程。

break功能的解决方案

在Scala中,虽然没有break关键字,但是存在break函数,即break()。注意,关键字和函数是不一样的。
在使用break()时,会在循环中抛出一个异常:

package com.test.scala01
import util.control.Breaks._
object Test2 {
   
	def main(args: Array[String]): Unit = {
   
		while (true) {
   
			break();//此处抛出异常
		}
	}
}

在这里插入图片描述
其实,大家看到这应该就有些眉目了,Scala是通过异常处理这种方式来实现和break关键字一样的功能。所以,既然抛出了异常,就要异常处理。
当然,这里需要使用breakable()来接收异常,这是一个高阶函数,高阶函数可以接受代码段作为参数:

package com.test.scala01
import util.control.Breaks._
object Test2 {
   
	def main(args: Array[String]): Unit = {
   
		breakable(
			while (true) {
   
				break();
			}
		)
	}
}

这样异常就被捕获了:
在这里插入图片描述
不过在breakable()的小括号中写好几行代码怪怪的,所以也可以用大括号:

package com.test.scala01
import util.control.Breaks._
object Test2 {
   
	def main(args: Array[String]): Unit = {
   
		breakable {
   
			while (true) {
   
				break();
			}
		}
	}
}

查看breakable()源码就能看出这种方式实现的原理:
在这里插入图片描述
图中的op就是自己写的代码块,在程序运行时,直接把这些括号内的代码都作为参数传至try语句的后面,通过try-catch来实现循环中断。
注意:不是所有的函数的小括号都可以换为大括号!!

continue功能的解决方案
  • 循环守卫(while循环没有守卫。。。)
  • 在循环体内使用if语句加以解决

函数

注意:函数式编程和函数是两个概念

函数的基本语法

def 函数名 ([参数:参数类型],······)[[:返回值类型]=] {
   
	语句······
	return 返回值
}
  • 函数声明关键字为def(definition)
  • [参数名:参数类型],···表示函数的输入(也就是参数列表)可以有也可以没有。如果有,多个参数使用逗号间隔
  • 函数返回值可有可无
  • 若函数体只有一行,则大括号可省略

返回值有多种情况:

  • 明确给出返回值 :返回值类型=
  • 返回值类型不确定,使用类型推导完成 =
  • 没有返回值,return不生效
  • 如果没有return,默认以执行到最后一行的结果作为返回值
  • 若声明返回值为Unit,那么不论return什么东西,返回值一定为Unit,即,也就是()

如果写了return,那么就必须要指明返回类型,而不能只写=让编译器推导类型
错误写法(浅灰色内容是IDEA的自动类型显示,不是代码部分):
在这里插入图片描述
正确的写法:
在这里插入图片描述

可变参数

顾名思义,可变参数是指向函数中传入的参数的数量是可以变化的,底层是通过集合(类似于Java中的数组)来进行接收参数的。

格式

*表示此参数为可变参数

def main(args: Array[String]): Unit = {
   
	printString("aaa", "bbb", "ccc")
}
def printString(s: String*): Unit = {
   
	println(s)
}

运行结果:
在这里插入图片描述

注意
  • 一个函数中的可变参数最多只能有一个
  • 可变参数必须置于最后

思考为何如此?

参数默认值

def printString(name: String = "nan"): Unit = {
   
	println(name)
}

使用时可以直接调用无需赋值:printString()

带名参数

在调用函数时带有参数的名称
例如:
函数主体

def printString(name: String = "nan", age: Int): Unit = {
   
	println(name + age)
}

调用:

printString(age = 12)

这种方式可以指定向某参数传值而无需考虑参数的先后顺序

实际使用中,函数可能参数众多,且均带有默认值,此时若只修改某一两个参数,便可通过带名参数的方法实现。

无参数

情况一

声明时有(),但参数列表为空:

def he(): Unit = {
   
	println(111)
}

调用时括号可以省略:

def main(args: Array[String]): Unit = {
   
	he()
	he
}
情况二

声明时无()

def he: Unit = {
   
	println(111)
}

那么在调用时()必须省略:

def main(args: Array[String]): Unit = {
   
	he
}

过程

过程(procedure)也是函数,但其返回类型为Unit,此时等号可以省略。

def he(num: Int) {
   
	print(num)
}

匿名函数

如果只关心函数的逻辑处理过程,而不关心函数名称,那么对于函数名称的声明也可以省略!当然,形式上要发生一些变化
普通函数:

def printName(name: String): Unit = {
   
	println(name)
}

匿名函数:

(name: String) => {
   
	println(name)
}

通常而言,匿名函数会结合高阶函数使用。高阶函数可以接受函数作为参数,如下所示:

object myClass {
   
	def main(args: Array[String]): Unit = {
   
		demo((name: String) => {
   
			println(name)
		})
	}
	//高阶函数 接受函数作为参数
	//func为接受的函数名 String为接受的函数的参数
	//第一个Unit为接受的函数的返回值类型
	def demo(func: String => Unit): Unit = {
   
		func("nan")
	}
}

匿名函数的简化原则

  • 参数类型可省略,由形参自动推导
    例:
object myClass {
   
	def main(args: Array[String]): Unit = {
   
		//name类型省略
		demo((name) => {
   
			println(name)
		})
	}
	
	def demo(func: String => Unit): Unit = {
   
		func("nan")
	}
}
  • 在参数类型省略后有且只有一个参数时,()可以省略,若无参数或参数数量大于1则不能省略
    例:
object myClass {
   
	def main(args: Array[String]): Unit = {
   
		//name类型省略
		//只有一个参数 圆括号可省略
		demo(name => {
   
			println(name)
		})
	}
	
	def demo(func: String => Unit): Unit = {
   
		func("nan")
	}
}
  • 匿名函数体若只有一行则大括号可以省略
    例:
object myClass {
   
	def main(args: Array[String]): Unit = {
   
		//name类型省略
		//只有一个参数 圆括号可省略
		//函数体只有一行 大括号可以省略
		demo(name => println(name))
	}
	
	def demo(func: String => Unit): Unit = {
   
		func("nan")
	}
}
  • 若参数只出现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值