Scala是学习Spark的基础。比java更面向对象。
Scala基于Java的,其编译都依赖于JVM,使得Scala也能够跨平台运行。
Scala的SDK拥有Java的部分类库,以及自己的特有类库,和一些Java的封装类库。
1.下载Scala并配置环境变量。
2.创建Maven新工程,增加Scala支持。
即可开始书写Scala代码。
Scala大部分性质与Java类似
一、基础
由于已经学习过Java基础,相似部分无需过多介绍。
1.1定义变量(类型可省略,编译器会推导)
var 变量 [:类型] = 初始值
val 常量 [:类型] = 初始值
1.2标识符(基本同Java)
1.3输出字符串(前两种与Java一样,只总结特别的用法(便于写sql?))
var s =
"""
|Select
| name ,dept
| from emp
| where *****
|""".stripMargin //默认以|为连接符
1.4键盘输入(类似Algs4(橘皮算法4)里的标准输入)
var a = StdIn.readLine()
var b = StdIn.readShort()
var c = StdIn.readDouble()
2.数据类型
Scala中一切数据都是对象,包括后文的函数,都是对象,都是Any的子类
其中数据类型分为AnyVal(数值)和AnyRef(引用)两大子类
Unit是一个数据类型,对应void 但是Unit的对象是() 而void在java只是关键字
Null也是一个类型,只有一个对象null
Nothing是所有数据类型的子类,用在一个函数没有明确返回值时使用。
对于Scala的数值类型的自动转换
(1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成 精度大的那种数据类型,然后再进行计算。
(2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动 类型转换。
(3)(byte,short)和 char 之间不会相互自动转换。
(4)byte,short,char 他们三者可以计算,在计算时首先转换为 int 类型。
3.Scala中的流程控制
if-else 需注意的是,由于定义函数的方式问题,实际上,if-else是可以有返回值的。返回值会取决于最后一行内容。
用if-else可以实现Java中的三元运算符
var res = if( ? ) conditionA else conditionB
Swtih在Scala中并不存在,需要模式匹配处理。(后续补充)
for循环 注意Scala的特殊语法即可,用法与Java基本类似,有少许扩展应用
for( i <- 1 to 5){
println(i) //12345
}
for( i <- 1 until 5){
println(i) //1234
}
for( i <- 1 to 3 ; j = 4 - i){
println(i +"" + j )
}//可一次性定义i j,构成嵌套循环
var res = for( i <- 1 until 5) yield {
i * 2
} //res ={2,4,6,8,10}
4.函数定义
def sum( x : Int , y : Int ) : Int = {
x + y
}
函数的化简原则(看得懂就行,推荐使用柯里化)
(1)return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
(2)如果函数体只有一行代码,可以省略花括号
(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
(4)如果有 return,则不能省略返回值类型,必须指定
(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
(6)Scala 如果期望是无返回值类型,可以省略等号
(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
5.高阶函数
5.1将值作为传递(参数为值,常规)
def f1(): Int = {
println("f1-working")
10
}
def f2(a: Int) = {
println(a)
}
f2(f1()) //f1-working 10
5.2将函数/代码块作为参数传递
def ff(a: String): Int = {
println("ff-working")
a.toInt
}
def ff2(op: String => Int)(s: String) = {
println("ff2-working")
println(op(s))
}
ff2(ff)("111")
//ff2(ff())("111") 错误!这代表着你要调用ff
5.3函数作为返回值传回
def func(): String => Int ={
def stoint(string: String): Int ={
string.toInt
}
stoint
}
var aa = func()
println(aa("11"))
5.4匿名函数
(x:Int) => {函数体}
(1)参数的类型可以省略,会根据形参进行自动的推导
(2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参 数超过 1 的永远不能省略圆括号。
(3)匿名函数如果只有一行,则大括号也可以省略
(4)如果参数只出现一次,则参数省略且后面参数可以用_代替
练习 1:定义一个匿名函数,并将它作为值赋给变量 fun。函数有三个参数,类型分别为 Int,String,Char,返回值类型为 Boolean。 要求调用函数 fun(0, “”, ‘0’)得到返回值为 false,其它情况均返回 true。
(a:Int,b:String,c:Char) => {
if ((a==0)&&(b.equals(""))&&(c.equals('0'))){
true
} else {
false
}
}
练习 2: 定义一个函数 func,它接收一个 Int 类型的参数,返回一个函数(记作 f1)。 它返回的函数 f1,接收一个 String 类型的参数,同样返回一个函数(记作 f2)。函数 f2 接 收一个 Char 类型的参数,返回一个 Boolean 的值。 要求调用函数 func(0) (“”) (‘0’)得到返回值为 false,其它情况均返回 true。
def func(int: Int):String=>(Char=>Boolean)={
def f1(string: String):Char=>Boolean={
def f2(char: Char):Boolean={
if ((int == 0) && (string.equals("")) && (char.equals('0'))) {
true
} else {
false
}
}
f2
}
f1
}
//柯里化
def func(int: Int)(string: String)(char: Char) ={
if ((int == 0) && (string.equals("")) && (char.equals('0'))) {
true
} else {
false
}
}
6.闭包&柯里化
闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的 环境,称为闭包
函数柯里化:把一个参数列表的多个参数,变成多个参数列表。
//闭包?用于获得一个普适性和特异性的一个平衡
//例如许多数据要进行一个加法
def addNormal(a: Int, b: Int) = a + b
//但是这样每次都要传入2参数 如果大多数数据一样,我们只需要针对特异性
def addByOne(a: Int) = a + 1
//这样的话可以有特异性,但是拓展麻烦于是兼顾特异性与普适性 嵌套
def addByUnknown(a: Int): Int => Int = {
def addb(b: Int): Int = {
a + b
}
addb
}
像上述的addByUnknown,就实现了一个闭包。当我们只执行
var addFive = addByUnknown(5) 此时我们调用了该函数,传进5 ,但是此时
var res = addFive(123) 此时我们调用addFive时,相当于调用的addb
按照之前学习的java来说,此时我们调用的第一个函数已经在栈帧里消失了,那么我们传进去的5是无法到达addFive的。但是这就是函数式变成的特性,addFive访问到了外部的变量。这就是闭包。
//简化
def addByUnknown1(a: Int)(b: Int): Int = a + b //柯里化 推荐
//实现了创造特异性函数 ,但是我们还可直接使用
// var addFive = addByUnknown(5) 可
// var addFive = addByUnknown1(5) 不可 因为返回值是Int,需要有个_占住b
// var addFive = addByUnknown1(5) _ 可
// var addFive = addByUnknown2(5) 可
// var addFive = addByUnknown2(5) _ 不可 因为其返回值是Int => Int
// println(addFive(100))
// var addSix =addByUnknown(6)
// var addSix =addByUnknown1(6) _
// var addSix =addByUnknown2(6)
// var addSix =addByUnknown2(6) _
// println(addSix(2))
// println(addByUnknown1(2)(3))
//即使用一个函数,可以创建出我们所需要的特异性函数。
7.函数递归
scala 中的递归必须声明函数返回值类型
//递归 阶乘 正常递归
def func0(int: Int):Int ={
if (int == 0) {
return 1
}
int *func0(int - 1)
}
println(func0(5))
//尾递归
def func1(i: Int):Int={
@tailrec
def loop(int: Int,res:Int):Int={
if (int == 0){
return res
}
loop(int-1,res*int)
}
loop(i,1)
}
//尾递归实现 scala的编译支持,不会StackOverflow
//但是java并不支持,可以自己写个接口,用于支持尾递归
8.控制抽象
//1.传值参数 只是将值传递给函数 ez
def ff():Int = {
println("ff work")
10 //传值
}
def printff(a : Int): Unit ={
println("a :"+a)
println("a :"+a)
}
printff(ff()) //传个值
println("==========================")
//2.传名参数
//(a : =>Int) 要求传进来的代码块,返回值是Int
def function(a : =>Int): Unit ={
println("a: "+ a)
println("a: "+ a)
}
function(ff())
function({
println("kjs;ofij;soijf")
2333
})
/*def function1(a : ()=>Int): Unit ={
println("a: "+ a)
println("a: "+ a)
}*/
// function1(ff()) 错误写法,因为类型并不是一个()=>的函数
/*
对于传值参数 输出结果为
ff work a :10 a :10
而对于传名参数
ff work a: 10 ff work a: 10
多调用了一次a ,因为对于传值,只执行了一次ff,启动以后,将返回值10给了printA
而对于传名,执行了两次。相当于将a替换成ff() 那么每次提到a 就会变成ff()执行一次。
*/
9.懒加载
lazy val res = sum(1,2)
println("111111111111111")
def sum(i: Int, i1: Int):Int ={
println("sum方法执行中")
i + i1
}
println(res + " 打印出res结果")
//如果没有lazy 此时是无法执行的,因为在加载res时,还没有定义sum,是无法定义的。
//同时注意 lazy只能定义val的定值
类似单例模式中的懒汉模式