SCALA基础
-
面向对象和面向函数的集成
!!!!一切值都是对象,一切函数都是值!!!!
-
函数时编程,一切都是函数
-
数学式的语法思维
——————————————————————
IDEA开发环境准备:
1、安装JDK、SCALA环境
2、IDEA安装scala插件
3、创建maven工程,创建目录,设置为root source目录
4、创建文件,后缀为scala
5、scala可以直接与java交互
——————————————————————
——————————————————————
1、标识符
- 字母数字标识符:使用字母或者下划线开头,$符号在SCALA中也被看做是字母,但应该尽量避免定义$开头的变量,因为SCALA存在保留变量以$开头
- 符号:符号标识符包含一个或多个表示符,SCALA编译时会将符号转换为$开头的变量名,如
:->
会被转义为$column$minus$greater,可以看出会转义为$英文单词,因此在java中调用scala程序时要使用转义后的符号
2、import导包
- SCALA的import可以出现在任何地方,有效范围:从声明处开始到程序最后结束。
- SCALA会默认导入java.lang._ ,scala._包,因此以scala开头的包使用时会省略包名的引用。
3、变量与常量
-
vaR定义变量
- 变量定义时必须赋值:
var A:Int =1
- 变量定义的类型可以不指定,自动推断
- 变量空值是_下划线,定义为空值时必须指定类型:
var A:Char=_
(boolean空为false,数值空为0,字符空为?)
- 变量定义时必须赋值:
-
vaL定义常量
- val和def都可以定义常量,但val只初始化一次,def每次都会初始化
- 常量的引用地址是不可改变的
- 因为只是一层引用地址,因此使用复合类型的常量时,复合类型内部的值是可变的。
变量类型与java基本相同,但是SCALA中没有primitive基本类型,所有的值也都是对象,可以理解为只有包装类。
emmmm ,还支持很奇怪的多行字符串 """i am multiparaph string"""
4、变量类型结构
-
Any 顶级类型
-
AnyVal
- Unit
- Byte、Short、Int、Long、Float、Double、Char、Boolean
- 空值:Nothing
-
AnyRef(java.lang.Object)
- List
- Option
- MyClass
- 空值:Null
-
5、类型别名
- type关键字,type 别名=类型 ,类似C语言中的typedef
typedef sturct{}mystruct stu
——————————————————————————————
流程语句语法
1、while循环
var i:Int=0; while(i<100){ println(i) i=i+1 } do{ println(i) i=i+1 }while(i<200)
2、for循环
val length:Int=100 for(i:Int <- 1 to length){ println(i*100) } for(i:Int <- 1 to(length,step)){ ... } for(i:Int <- 1 to length by step)){ ... } for(i:Int <- 1 until length){ println(i*100) } for(i <- 1 until(length,step)){ ... } //until 和 to 都是一种函数,也可以通过i.until(length)调用 //until 和 to 创建的区间是相同的
3、中断break和breakable
- 关于Break和Continue,要导入包才可以使用
impot import scala.util.control.Breaks.break impot import scala.util.control.Breaks.breakable
- breakable(语句块),内部嵌套break即可跳出本次循环
- 卸载breakable外面的break直接跳出循环
- breakable即可打断的语句块,相当于在循环中又嵌套了一层可被打断的语句块,将代码写在其中即可实现continue
4、多条件循环
- 多重循环条件+分号;
val num:Int = 10; for ( i: Int <- 1 to 10; if i%2==0;if i>5 ) { println( i * 100 ); }yield i; //使用 yield 将fot将具有返回值,yield中记录的是符合条件的步变量i的值的集合。 //可以看出for也是个函数
5、数组
泛型定义,中括号(java是尖括号)
数组,取值用小括号(java是中括号)
Array 不可变集合 ,元素内容可变,数组长度不可变
ArrayBuffer 可变长数组
- 万物皆函数,因此数组也是使用的小括号()
- 定长数组
//方式1 var a:Array[String]=new Array[String](3) a(0)="hello" a(1)="scala" a(2)="world" //方式2 var a2=Array("hello","scala") //方式三,区间数组 var a3=Array.range(1,10,2) //1,3,5,7,9
- 变长数组
import scala.collection.mutable.ArrayBuffer var arrbuf:ArrayBuffer[String]=new ArrayBuffer[String]
6、元组
- Tuple可以包含不同类型的值,类型以实际存储的元素类型为准
- 元组最多支持22个元素
- 使用name._1访问name元组的第1个元素,元祖没有0号位
- 元组的类型:元组的实际类型取决于它的分量的类型,比如的
(2,"string")
类型实际为Tuple2[Int,String]
,而(‘u’,’r’,”the”,1,4,”me”)
的类型为Tuple6[Char,Char,String,Int,Int,String]
Tuple[长度](类型列表)
//元组声明方式一 var tp1 = ("Mike", "123 ABC street", 58) println(tp1._1) println(tp1._2) println(tp1._3) //迭代元组 tp1.productIterator.foreach{ i =>println("Value = " + i )} //元组声明方式二 var tp2 = new Tuple3("Mike", "123 ABC street", 58) //元组声明方式三 def mike = "Mike" -> 5 //键值对形式 //输出scala.Tuple2 mike.getClass //将元组元素依次赋给三个变量 val(name, address, age) = tp1 println(name) println(adrress) println(age) //元祖还可以这么玩 var yz=(1,List(2,3),"xixi") val (id,order_id,name)=yz //有点类似于样例类
7、集合Collection
1、序列Seq:List,Stack,Queue,Array
2、集合Set
3、映射Map
8、mutable和immutable不可变集合
复杂类型大都分为两类,MUTABLE和IMMUTABLE,可变的和不可变的,如ArrayBuffer就是一个可变集合,默认scala会选择不可变集合
-
包名:scala.collection.mutable|immutable.
-
可变集合:可以修改、添加或移除一个集合的元素
名称 | 可变/不可变 | 示例 |
---|---|---|
Buffer | mutable | val buffer = scala.collection.mutable.ArrayBufferInt; buffer+=(2,3) |
Array | mutable | val arr=Array(1,2,3) |
List | immutable | val lst=List(1,2,3) |
Map | mutable | val stu= Map("name" -> "Jason", "age" -> "18") |
Set | mutable/immutable | val set=Set(1,2,3) |
Vector | immutable | val v=Vector(1, 3, 5, 7, 11, 13) |
Stack | mutable/immutable | val st=scala.collection.mutable.Stack(1,2,3) //堆栈,先进后出 |
Queue | mutable/immutable | val q=scala.collection.mutable.Queue(1,2,3) //队列,先进先出 |
BitSet | mutable/immutable | val bit=scala.collection.mutable.BitSet(3,2,0) //位集合 |
ListMap | immutable | val map = scala.collection.immutable.ListMap(1->"one", 2->"two") |
HashSet | mutable | val set= scala.collection.mutable.HashSet(1,2,3) |
HashMap | mutable | val stu= scala.collection.mutable.HashMap("name" -> "Jason", "age" -> "18") |
9、集合常用操作
1)List常用操作
-
追加操作(新对象)
- 用于不可变对象,会生成新对象:
+:
新加变量为主,在新变量后追加整个集合:+
集合为主,在集合的后面追加新变量
-
扩充操作(本对象)
- 用于可变对象,直接修改本list:
+=
集合为主,在集合后面追加新变量+=:
新加变量为主,在集合的前面追加新变量++=:
新加变量为主且该变量为List类型,在集合的前面追加新变量
-
分组操作
- grouped n(等价于list.grouped(n)),n个为一组
- sliding(n,step),每次显示n个,,起始位置s是0。滚动显示,默认步长step为1
2)Set常用操作
-
SortSet ,可以排序的set
-
Set,去重的
-
增删操作
+=
修改自身,追加一个内容-=
修改自身,删除一个内容--
删除一系列内容++
增加一系列内容
-
逻辑运算
- | 求并集intersect
- & 求交集union
- &~ 求差集diff
3)Map常用操作
4)Stream&Vector
- Stream是惰性List,参数值只会在使用到的时候才会初始化
- Vector是连续存储的List,访问效率比随机存储的list更高
10、空值
- Nil 继承了List[Nothing类],指空List
- None 继承了Option[Nothing] 类,指的是Option的空值
- Null是指空引用类型,null是引用的空值
- Nothing 是Any的子类的,代表类型的的空(指没有类型,Null空类型也是一种类型),类型为空的变量必须是空的,没有实例
函数
一切函数都是值,一切值都是对象,一切对象都可以是函数
def 函数名([参数列表]):[返回值类型]={ print("我是个函数") return [表达式] //return 是可选的,如果不定义,自动返回最后一个行的值 }
1)函数调用
-
传值参数
//假设调用时为square(1+2) def square(x:Int):Int={ print(x) //3 x*x //3*3 }
-
传名调用,x不计算,仅传递代码体
- 与闭包搭配,外面修改x后,函数每次都会重新计算参数值因此会实时更新X
def square(x: => Int):Int={ //注意空格 print(x) //1+2,在print中计算 x*x //(1+2)*(1+2) }
!!!一切皆地址,函数也是地址,函数名只是引用的函数地址!!!
2)函数参数/函数返回值
语法结构:
def 函数名(形参名:(形参函数参数类型表)=> 形参函数返回值类型):[返回值]={ //调用形参函数 形参函数名(参数) }
demo实例
def myfun(fun:Int => Unit):Unit={ fun(1)//注意,这里的fun是形参函数名 } def fun(x:Int):Unit={ print(x) } myfun(fun(1)) //:Unit 可以省略 ,默认就是无返回值 //参数为空 def NullPara(){} 或者 def NullPara(null: =>String):String{}//省略了类型 //常用高阶函数 val l=List(1, 2, 3, 4, 5, 6, 7, 8, 9) l.map(_*2).foreach(println) l.map(_*2).filter(_>8).foreach(println)l.fold(0)((sum,i)=>sum+i) l.take(4).foreach(println)//非高阶 println(l.reduce(_+_)) println(l.reduce(_-_)) println(l.max) println(l.count(_>3)) println(l.min) println(l.sum) val a=List(1,2,3,4) val b=List("A","B","C","D") println(a zip b) val c=List("A","B","C","D","E") println(a zip c) val d=List(1,2,3,4,5) println(d zip c) List("zhangsan","lisi","wangwu").zip(List(100,90,75,83)) val f=List(List(1,2),List(3,4),List(5,6)) println(f.flatten) println(f.flatMap(_.map(_*2)))
3)匿名函数
- 关键字符:
=>
- 左边为(函数参数),右边为{ 函数体 }
(x:Int)=>{print(x)} //因为匿名,没有引用变量,没法直接使用,要么赋值给变量,要么作为实参 myfun((x:Int)=>{print(x);print(x+x)}) //一般使用在函数参数处 //多条语句使用分号隔开
- scala内置的大部分接口函数都支持匿名函数,可以自定义匿名函数作为foreach和map函数的参数
4)函数变量& def关键字
a)函数变量
//函数有参时 var funVar=myfun _ //相当于引用了函数地址 var funvalue=myfun //报错 def funbody=myfun _ //相当于重名名函数 //使用下划线缺省函数参数 //可以说val其实是拿到了函数de返回值 //函数无参时 var funvar=myfun //无参myfun等价于myfun(),执行函数并返回值给funvar funVar("xixi")
- var或者val定义的函数变量:定义时执行一次函数体,之后每次调用只能得到函数体的返回值return值
- def定义的函数变量:定义时不执行函数体,之后每次调用都会完整的运行一遍函数体。
b)def关键字
- def不仅仅用于定义函数和函数变量
5)嵌套函数
-
函数中可以再次定义函数,这个子函数只有在本函数内可见。
def factorial(i: Int): Int = { def fact(i: Int, accumulator: Int): Int = { if (i <= 1) accumulator else fact(i - 1, i * accumulator) } fact(i, 1) //不能在factorial()之外调用 }
6)cury柯里化
demo实例
def sum(x:Int)(y:Int)={ print(x+y) }
柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
- 子函数依次产生,每个子函数根据当前参数返回新的子函数。
- 参数1--->子函数1(参数2)--->子函数2(参数3) >>>>>最终返回函数的最终返回值
- 更加灵活的调用同一个函数
灵活运用demo
def sum(x:Int)(y:Int)={ print(x+y) } def f1(x:Int)=sum(1)(x) def f2(y:Int)=sum(x)(2) def f3(z:Int)=sum(3)_ def f4(g:Int)=sum_(4) def f5(()()=>)
7)隐式函数/隐式参数
demo参数
implict var today="2018-10-25" def mkLog(arg:String,implict date:String){ print(date+" "+arg) } mkLog("Success") //运行成功,作用域内找到合适类型的参数,使用隐式参数 mkLog("Success","2018-1-1") //运行成功
demo 函数
"100"/10 //ERROR 类型错误 def String2Int(str:String)={ str.toInt } "100"/10 //类型错误-->发现合适的类型转换函数-->调用-->隐式转换 res1:10
- 隐式转换和调用只会发生在发生类型错误的时候
- 两者都需要有implict声明
- 只能发生在当前作用域内进行值和函数的查找
8)命名参数&参数缺省值
-
命名参数:即在调用函数的时候声明值的形参名
def function(str1:String,str2:String){ print(str1+str2) } function(str2="xixi",str1="haha"); //res1:hahaxixi
-
参数缺省值:即在定义函数的时候给出参数的default值
def sum(a:Int=1,b:Int=2){ print(a+b) } sum() //res1:3 //与命名参数一起使用更佳 sum(b=10) //res2:11 sum(10) //res3:12
常用高阶函数
- 高阶函数,以函数作为参数
————————————————————————————————
1、map
- 集合的函数映射,生成新的集合
2、foreach
- 相当于for循环内部函数体
3、filter
- 自定义函数过滤器
lst.filter((x:Int)=>x>50) lst.filter(_>50) //参数函数返回值为true的元素才会被保留
4、reduce
lst.reduce(_+_) lst.reduce((x:Int,y:Int)=>{x+y})
5、fold
lst.fold(5)(_+_) //与reduce相似,但有一个预先值作为第一个值 //foldRight 从右侧开始
6、zip
//类似于拉链,逐个嵌套 var lst2=lst.map(_*2) lst1.zip(lst2) //生成了键值对列表 //长度以最短的list为准
7、flatten
//将复杂类型的内部嵌套展开 //只保留最外层的复杂类型结构 var map1=lst.map(x=>List(x)) map1.flatten map1.flatten(_.map(_*2))
8、flatMap
//先对子复杂结构进行map //然后flatten各子元素map的结果 map1.flatMap(_.map(_*2))