Scala
Author: Lijb
Email: [email protected]
Scala是一门多范式的编程语言,同时支持面向对象和面向函数编程风格。它以一种优雅的方式解决现实问题。虽然它是强静态类型的编程语言,但是它强大的类型推断能力,使其看起来就像是一个动态编程语言一样。Scala语言最终会被翻译成java字节码文件,可以无缝的和JVM进行集成,同时可以使用Scala调用java的代码库。
编程指南:https://docs.scala-lang.org/tour/tour-of-scala.html
Scala环境搭建
下载scala:https://www.scala-lang.org/download/2.11.12.html
Windows版本安装
-
点击scala-2.11.12.msi傻瓜安装
-
配置Scala的环境变量SCALA_HOME变量
SCALA_HOME=C:\Program Files (x86)\scala PATH=%SCALA_HOME%/bin
-
打开命名窗口
C:\Users\HIAPAD>scala Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_161). Type in expressions for evaluation. Or try :help.
scala>
CentOS安装
-
下载scala-2.11.12.rpm
-
安装配置Scala
[root@CentOS ~]# rpm -ivh scala-2.11.12.rpm Preparing... ########################################### [100%] 1:scala ########################################### [100%] [root@CentOS ~]# scala Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_171). Type in expressions for evaluation. Or try :help.
IDEA集成Scala开发环境
- 在File>Setting>Plugins点击install plugin from disk选项,选择scala-intellij-bin-2018.2.11.zip安装成功后,重启IDEA
Scala变量
参考:https://docs.scala-lang.org/resources/images/tour/unified-types-diagram.svg
scala> var i=1
i: Int = 1
scala> var i="abc"
i: String = abc
scala> var i: ="abc"
:11: error: type mismatch;
found : String("abc")
required: Int
var i:Int="abc"
^
Scala所有的变量在声明时可以省略类型,由编译器自动推断,一旦编译后,类型固定,不可以更改
scala> var i=1
i: Int = 1
scala> i="abc"
:12: error: type mismatch;
found : String("abc")
required: Int
i="abc"
^
scala> var i:Int=1:Int
i: Int = 1
var和val区别?
var修饰的变量值是可以改变的
val修饰的值,不可以改变类似于java中final
scala> val i=10
i: Int = 10
scala> i=1000
:12: error: reassignment to val
i=1000
^
字面值类型相互转换
scala> val face: Char = '☺'
face: Char = ☺
scala> val i:Int =face
i: Int = 9786
scala> val c:Char = i.toChar
c: Char = ☺
变量定义
var|val 变量名[:变量类型]=变量值[:变量类型]
var|val 变量名 = 变量值 //使用Scala的类型推断
var a:Int = 1
var str:String = "hello world"
var pair:(Int,String) = (1,"测试") //元组
var b=1
var i:Int=1
var j=1:Byte
var tuple=(1,2)//元组
元组在Scala中是由多种类型的变量组装而成,一旦赋值,不可更改
scala> var pair:(String,Int)= "zhangsan" -> 10
pair: (String, Int) = (zhangsan,10)
scala> var pair:(String,Int)= ("zhangsan", 10)
pair: (String, Int) = (zhangsan,10)
scala> var pair= ("zhangsan", 10)
pair: (String, Int) = (zhangsan,10)
scala> var pair= "zhangsan" -> 10
pair: (String, Int) = (zhangsan,10)
scala> var tuple:(Int,Int,String)=(1,2,"Hello")
scala> tuple._1
res4: Int = 1
scala> tuple._2
res5: Int = 2
scala> tuple._3
res6: String = Hello
scala> tuple._3="world"
:12: error: reassignment to val
tuple._3="world"
^
算术运算符
算术运算符:+、-、*、/、%、
关系运算符:==、!=、>、<、>=、<=
逻辑运算符:&&、||、!
位运算符:&(按位与)、|(按位或)、^(异或)、~(取反)、<<、>>、>>>(无符号)
赋值运算符:= 、
组合赋值:(算术|位运算=)
判断Scala的类型
var i=0
var sex:Any={
if(i>0){
true
}else{
"不知道"
}
}
if(sex.isInstanceOf[Boolean]){
println("你是布尔")
}else{
println("你是String:"+sex.asInstanceOf[String])
}
Scala的分支语句
if条件分支
if(布尔表达式 1){
// 如果布尔表达式 1 为 true 则执行该语句块
}else if(布尔表达式 2){
// 如果布尔表达式 2 为 true 则执行该语句块
}else if(布尔表达式 3){
// 如果布尔表达式 3 为 true 则执行该语句块
}else {
// 如果以上条件都为 false 执行该语句块
}
while循环控制
//求1到100的和
var i=0 //可省略
var n=0 //可省略
while(i< 100){
i+=1
n+=i
}
print("while循环后结果为:"+n)
do-while循环
//求1到100的和
var i=0 //可省略
var n=0 //可省略
do{
i+=1
n+=i
}while(i<100)
print("do->while循环后结果为:"+n)
for循环
//求1到100的和
var i=0 //可省略
var n=0 //可省略
for(i<-1 to 100 by 2){
n+=i
}
println(n)
for 循环 中你可以使用分号 (;) 来设置多个区间,它将迭代给定区间所有的可能值(实现嵌套)
//100以内能被3整除不能被5整除的和
var i=1
var result=0 //可省略
for(i<-1 to 100 if i%3==0 && i%5!=0){
result+=i
}
print(result)
-
for循环集合
val list = List(1,2,3,4,5,6) for (a<-list){ println(a) }
-
for循环过滤
//例一:百钱买百鸡 var x = 0 //可省略 var y = 0 //可省略 for (x <- 0 to 33) { var maxy = (100 - x * 3) / 2 for (y <- 0 to maxy) { var z = 100 - x - y; if (3 * x + 2 * y + z / 3.0 == 100 && x + y + z == 100) { println("公鸡:" + x + "只;" + "母鸡:" + y + "只;" + "小鸡:" + z + "只。"); } } } //例二: var a = 0; //可省略 val list = List(1,2,3,4,5,6,7,8,9,10); for(a<-list if a%2==0;if a<=4){ println(a) }
-
for使用 yield
//yied相当于提取出 var a = 0; val numList = List(1,2,3,4,5,6,7,8,9,10); // for 循环 var retVal = for{ a <- numList if a != 3; if a < 8 }yield a // 输出返回值 for( a <- retVal){ println( "Value of a: " + a );、 }
match-case
Scala中取消了switch-case语法,取而代之的是使用match-case语法,也称为模式匹配。
var sex="..."
var alias =sex match {
case "boy" => "男孩"
case "girl" => "女孩"
case default => "怪物"
}
println(alias)
default可以用_替换
Break语法 Scala 语言中默认是没有 break 语句,但是你在 Scala 2.8 版本后可以使用另外一种方式来实现 break 语句。当在循环中使用 break 语句,在执行到该语句时,就会中断循环并执行循环体之后的代码块。Scala 中 break 的语法有点不大一样,格式如下:
var a = 0;
val numList = List(1,2,3,4,5,6,7,8,9,10);
val break = new Breaks
break.breakable(
for (a<-numList){
println( "Value of a: " + a )
if(a==4){
break.break()
}
}
)
println( "After the loop" );
Scala函数
函数声明
def functionName ([参数列表]) : [return type]
标准函数
def sum(x:Int,y:Int):Int={
return x+y } //可以尝试省略返回值类型
def multi(x:Int,y:Int)={
x*y
}
def sayHi(name:String):Unit={
println("hi~ "+name)
}
指定函数名
def main(args: Array[String]): Unit = {
println(sum(5,2))
}
def sum (x:Int,y: Int): Int ={
return x+y
}
可变长参数函数
要求必须放置在所以参数的最后
def main(args: Array[String]): Unit = {
println(sum(1,2,3,4,5,6,7,8,9,10))//结果:55
}
def sum(args: Int*): Int = {
var result = 0
for (i <- args) {
result += i
}
return result
}
//打印参数WrappedArray(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 说明传入的参数是个一个args数组
默认参数值
def main(args: Array[String]) {
sayHi("ww")
sayHi()
}
def sayHi(name:String="zhangsan"):Unit={
println("hi~ "+name)
}
内嵌函数
def main(args: Array[String]): Unit = {
println(factorial(5))
}
def factorial (x:Int)={
def mulit(i:Int):Int={
if(i>0){
i*mulit(i-1)
}else{
1
}
}
mulit(x)
}
匿名函数
匿名函数 Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。使用匿名函数后,我们的代 码变得更简洁了。本质是一个lambdar表达式
(x:Int,y:Int) => {x+y}
将一个函数变成变量
//函数
def fun(x:Int):Int={
return x+1
}
//改写成变量
var f1:(Int)=>Int=(x)=>x+1
val f2 = (x: Int) => x + 1
函数的返回值问题
1,声明函数的返回值类型,则在函数返回时一定要符合返回类型
2,可以不声明函数返回值类型,scala解释器会自动推断返回的数据类型
3,递归函数必须显式声明返回的类型
4,定义函数时没有等号= 则没有返回值。这类函数相当于一个执行过程
高阶函数
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数
- 接受一个或多个函数作为输入
- 输出一个函数
在数学中它们也叫做算子(运算符)或泛函。微积分中的导数就是常见的例子,因为它映射一个函数到另一个函数。
//求给定区间的整数和
def main(args: Array[String]): Unit = {
println(sumInts(2,5))
}
def sumInts(a: Int, b: Int): Int =
if(a > b) 0 else a + sumInts(a + 1, b)
}
//求给定区间连续的整数的平方和
def main(args: Array[String]): Unit = {
println(sumSquare(1,100))//结果为338350
}
def square(x: Int) = x * x
def sumSquare(a:Int,b:Int):Int={
if(a > b) 0 else square(a)+sumSquare(a+1,b)
}
map方法将一个函数应用到某个集合的所有元素并返回结果;foreach将函数应用到每个元素。
(1 to 9).map("^" * _).foreach(println _)
^^
^^^
^^^^
^^^^^
^^^^^^
^^^^^^^
^^^^^^^^
^^^^^^^^^
filter方法输出所有匹配某个特定条件的元素:
(1 to 9).filter(_ % 2 == 0).foreach(println _)
//结果:2 4 6 8
函数柯里化(Currying)
柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。
- 返回函数的函数
在上面高阶函数的例子中,我们通过def sumInts(a: Int, b: Int): Int = sum(x => x, a, b)、def sumSquared(a: Int, b: Int): Int = sum(x => x*x, a, b)来定义新的函数,上面这两个函数每次都要传入a和b两个参数到sum函数中,我们能否简化这些参数使得函数定义更简单呢?
我们可以通过返回函数的函数来简化参数:
/*当你调用sum1(2)(8)时,实际上是依次调用两个普通函数(非柯里化函数), 第一次调用使用一个参数x,返回一个函数类型的值,第二次使用参数y调用这个函数类型的值*/
def sum(f: Int => Int): (Int, Int) => Int = {
def sumF(a: Int, b: Int): Int =
if(a > b) 0
else f(a) + sumF(a+1, b)
sumF
}
//于是得到如下定义,这样就简化了参数
def sumInts = sum(x => x)
def sumSquared = sum(x => x * x)
def sumPowersOfTwo = sum(powerOfTwo)
- 多个参数列表
根据上面的例子,我们能不能更加简化,省去sumInts、sumSquared、sumPowersOfTwo这几个中间函数的形式呢?
通过sum(square)(1, 10)函数来替代sumSquared函数。
一般情况,这类函数的左结合的:sum(square)(1, 10) == (sum(square))(1, 10)。
我们可以这样定义sum函数:
def sum(f: Int => Int)(a: Int, b: Int): Int =
if(a > b) 0 else f(a) + sum(f)(a + 1, b)
这使得函数编写更加简洁。
一般的,多参数函数定义为def f(args1)...(argsn) = E,
当n > 1时,等同于def f(args1)...(args n-1) = {def g(argsn) = E; g}或者def f(args1)...(args n-1) = (argsn => E)。
如果重复这个过程n次,得到def f = (args1 => (args2 => ... (argsn => E) ) )。
这种函数定义称为柯里化(Currying)。
类和object
Scala中的类是用于创建对象的蓝图,其中包含了方法、常量、变量、类型、对象、特质、类,这些统称为成员。由于Scala没有静态方法和静态类,通过object去定义静态方法或者静态对象。当object和Class放在一个文件中时候称该object为伴生对象。
object(单例类)
object UserService {
def main(args: Array[String]): Unit = {
var us1=UserService
var us2=UserService
println(us1==us2) //结果为true-->说明us1和us2是同一个对象-->单例
UserService.sayHi()
us1.sayHi()
us2.sayHi()
sayHi()
}
def sayHi():Unit={
println("hello")
}
}
类定义
class Student {
var id=1
var name="zhangsan"
var age=18
var birtDay=new Date()
}
构造器
在scala中只有Class或者abstract Class才能有构造方法,Scala中声明在Class上的构造必须为最简构造,其他构造方法参数必须涵盖最简构造。
class User{
var id:Int = _
var name:String = _
def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
def sayHi(): Unit ={
println(id+" "+name)
}
}
-----
class User(var id:Int,var name:String){
def this(){
this(1,"张三")
}
def sayHi(): Unit ={
println(id+" "+name)
}
}
伴生对象
当类名和单例类名字一样的时候,我们把单例类称为伴生对象
class User{ }
object User {
def main(args: Array[String]): Unit = {
var u1=User
var u2=User
var u3=new User()
println(u1==u2)//true
println(u1==u3)//false
}
}
apply&unapply
Scala中的 apply 方法有着不同的含义, 对于函数来说该方法意味着调用function本身, 以下说明摘自
Programming in Scala, 3rd Edition
Every function value is an instance of some class that extends one of several FunctionN traits in package scala, such as Function0 for functions with no parameters, Function1 for functions with one parameter, and so on. Each FunctionN trait has an apply method used to invoke the function.
在Scala语言中, 函数也是对象, 每一个对象都是scala.FunctionN(0-22)的实例, 其中N是函数参数的数量, 例如我们定义一个函数并赋值给一个变量:
def main(args: Array[String]): Unit = {
println(fun(2)) //结果为2
var fun1:(Int)=>Int=(x)=>x+1
val fun2 = (x: Int) => x + 1
println(fun1.apply(2)) //结果为2
println(fun2.apply(2)) //结果为2
}
def fun(x:Int):Int={
return x+1
}
apply 方法用在object中一般作为工厂方法用于产生Class对象,通常,在一个类的半生对象中定义apply方法,在生成这个类的对象时,就省去了new关键字。
class Student (name:String) { }
object Student{
def apply(name: String): Student = new Student(name)
def main(args: Array[String]): Unit = {
var s1=new Student("张三")
var s2= Student("李四")
var s3=Student("李四")
println(s1) //com.baizhi.zpark.Student@47f37ef1
println(s2) //com.baizhi.zpark.Student@5a01ccaa
println(s3) //com.baizhi.zpark.Student@71c7db30
println(s1==s2) //false
println(s2==s3) //false
}
}
extractor object是具有unapply方法的对object。unapply方法可以看做是apply方法的相反操作,apply方法接受构造参数变成对象,而unapply方法接受一个对象,从中提取值。
object CustomerID {
def apply(name: String) = s"$name--${Random.nextLong}"
def unapply(customerID: String): Option[String] = {
val stringArray: Array[String] = customerID.split("--")
if (stringArray.tail.nonEmpty) Some(stringArray.head) else None
}
}
val customer2ID = CustomerID("Nico")
val CustomerID(name) = customer2ID
println(name) // prints Nico
extractor object 更多可参考 https://docs.scala-lang.org/tour/extractor-objects.html
抽象类/抽象方法
//抽象方法
abstract class Animal(name:String) {
def eat():Unit={
println("animal can eat...")
}
def sleep():String
}
//集成抽象类--覆盖里面的抽象方法
object AnimalImpl extends Animal {
override def sleep(): Unit = {
println("Animal sleeping 5 hours")
}
def main(args: Array[String]): Unit = {
sleep() //调用覆盖的方法
eat() //调用继承的抽象类中的非抽象方法
}
}
特质trait(java中的接口)
trait Speakable {
def speek():Unit
}
trait Flyable{
def fly():Unit
}
继承&实现
-
类-类、类接口
class Dog(name:String) extends Animal(name:String) with Speakable { override def sleep(): String = { "i'm a dog I sleep 8 hours" } override def speek(): Unit = { println("wang wang ~~") } override def eat(): Unit = { println("啃骨头") } } object Dog{ def apply(name: String): Dog = new Dog(name) def main(args: Array[String]): Unit = { var dog=Dog("小花") dog.eat() } } class Bird extends Flyable with Speakable { override def fly(): Unit = { println("i can fly") } override def speek(): Unit = { println("唧唧咋咋") } } object Bird{ def apply(): Bird = new Bird() def main(args: Array[String]): Unit = { var bird=Bird() bird.fly() } }
-
接口-接口
trait A{} trait C{} trait B extends A with C{}
接口动态植入
class Bird extends Flyable {//Flyable是trait
override def fly(): Unit = {
println("i can fly")
}
}
object Bird{
def main(args: Array[String]): Unit = {
var bird=new Bird() with Speakable {
override def speek(): Unit = {
println("唧唧咋咋")
}
}
bird.speek()
}
}
注意在覆盖有实现的方法必须添加overwrite一个类只能继承一个类with多个trait例如
Class A extends B with C with D{ }
上述的B可能是特质也可以是抽象类 但C、D必须是特质。
this&self-type
和java有所不同在Scala中class和object都可以使用this关键字代表自身。同时Scala支持给当前对象起别名
class Student(name:String) {
self => //起别名 用self代表当前对象
def sayHello(): Unit ={
println(this == self)
println("hello "+self.name)
}
}
object Student{
def apply(name: String): Student = new Student(name)
def main(args: Array[String]): Unit = {
var stu=Student("zhangsan")
stu.sayHello()
}
}
self-type是一种声明Trait必须混合到另一个Trait的方法,即使它没有直接扩展它。这使得依赖的成员可以在没有导入的情况下使用。
定义:特质可以要求混入它的类扩展自另一个类型,但是当使用自身类型(self type)的声明来定义特质时(this: ClassName =>),这样的特质只能被混入给定类型的子类当中。 如果尝试将该特质混入不符合自身类型所要求的类时,就会报错。
从技术角度上看,自身类型是在类中提到this时,对于this的假设性类型。
从实用角度上看,自身类型指定了对于特质能够混入的具体类的需求。如果你的特质仅用于混入另一个或几个特质,那么可以指定那些假设性的特质。
trait User {
def username: String
}
trait Tweeter {
this: User => // reassign this
def tweet(tweetText: String) = println(s"$username: $tweetText")
}
class VerifiedTweeter(val username_ : String) extends Tweeter with User { // We mixin User because Tweeter required it
def username = s"real $username_"
}
val realBeyonce = new VerifiedTweeter("Beyoncé")
realBeyonce.tweet("Just spilled my glass of lemonade")
可见性控制
Scale和java类型可以控制Scala中对象的可见性,例如下面表示创建一个公开类,任何包下的类都可以使用,同时该类下的成员变量都是公开。
class Student {
var id=1
var name="zhangsan"
var age=18
var birtDay=new Date()
}
-
private限定 private修饰的属性只能对本类可以使用,对于伴生对象可见 private修饰类,可继承并且继承之后可以访问父类属性和方法,除非父类的属性和方法加上private修饰符之后不可访问
class Student { private var id=1 var name="zhangsan" var age=18 var birtDay=new Date() } object Student{ def apply(): Student = new Student() def main(args: Array[String]): Unit = { val student = Student() student.id } }
-
protected限定 protected修饰的属性对本类/伴生对象可用,对于子类/子类的伴生对象可见
class Student { protected var id=1 var name="zhangsan" var age=18 var birtDay=new Date() } object Student{ def apply(): Student = new Student() } class SmallStudent extends Student{ } object SmallStudent { def apply(): SmallStudent = new SmallStudent() def main(args: Array[String]): Unit = { val student = SmallStudent() student.id } }
-
this限定 如果严格限定属性可以操作范围仅仅限制在本类,去除伴生对象的可见性,可以添加this限定
class Student { protected|private[this] var id=1 var name="zhangsan" var age=18 var birtDay=new Date() } object Student{ def apply(): Student = new Student() }
-
包限定 Scala还可以做包限定,表示id属性对Student的子类可见,其他类必须是同包下可见
package com.jiangzz.demo import java.util.Date class Student { protected[demo] var id=1 var name="zhangsan" var age=18 var birtDay=new Date() } object Student{ def apply(): Student = new Student() }
-
final限定
final可以修饰Class和方法,final修饰的类不可以被继承,修饰的方法不可以被覆盖,但是final修饰属性时没有效果,val修饰属性才时不可变的。
final class Student(name:String) {
final def sayHi(name:String):String={
"hello "+name
}
}
-
方法覆盖&重载 scala保留了java中的方法重载和覆盖功能具体使用和Java语法类似。
def fun(x:Int,y:Int):Int={ x+y } def fun(x:String,y:Int):String={ x+y } override def toString: String = { "..." }
函数对象(重点)
Partial Applied Function(部分应用函数)
函数对象这是一种特殊的对象,也有人称之为高阶函数。我们把不具有任何可改变状态的对象称为函数式对象。对于函数而言分为参数类型和函数体实现,Scala的函数对象就是一种特殊的class既可以担当函数计算职责又可以作为变量传递。Scala可以很方便的将函数转变成为函数对象。
scala> def sum(v1:Int,v2:Int):Int={
| return v1+v2
| }
sum: (v1: Int, v2: Int)Int
scala> val s1=sum(_:Int,1)
s1: Int => Int = $$Lambda$1291/1237850567@17d28c1d
scala> s1(3)
res12: Int = 4
scala> val s2=sum _
s2: (Int, Int) => Int = $$Lambda$1158/879220032@67710b92
函数对象
通常将s1和s2称为sum函数的部分应用函数(Partial Applied Function),仔细观察可以发现当sum转变为s1、s2对于Scala而言都是某种对象类型的变量。
对于s1而s1的类型就是Int=>Int类型变量;s2而言该s2的类型就是(Int,Int)=>Int类型。对于Scala而言Scala在做方法解析的时候底层会将方法转换为Function1~22对象类型,当程序编写一个方法事实上是在创建Function[1~22]对象,调用对象的方法就等价于调用Function的apply方法
scala> def sum(v1:Int,v2:Int):Int={
| return v1+v2
| }
sum: (v1: Int, v2: Int)Int
scala> val s=sum _
s: (Int, Int) => Int = $$Lambda$1295/814452367@1faa627c
scala> s.isInstanceOf[Function2[Int,Int,Int]]
res20: Boolean = true
scala> s.isInstanceOf[(Int,Int)=>Int]
res21: Boolean = true
scala> s.apply(1,2)
res0: Int = 3
以上代码等价于
scala> val s=new Function2[Int,Int,Int] {
| override def apply(v1: Int, v2: Int): Int = {
| return v1+v2
| }
| }
s: (Int, Int) => Int =
scala> s.apply(1,2)
res11: Int = 3
通过函数到对象的转换,我们不难发现,可以将任意一个函数类型转换为变量类型,函数转换对象类型的变量转换规则如下:
按照上述转换规则 将函数声明转换为变量类型,将函数实现用lambda表达式实现就可以实现将函数转变Function类型的变量。
class SumFunction extends ((Int,Int)=>Int){
override def apply(v1: Int, v2: Int): Int = {
return v1 + v2
}
}
object SumFunction{
def main(args: Array[String]): Unit = {
val s=new SumFunction()
println(s.apply(1,2))
println(s.isInstanceOf[Function2[Int,Int,Int]])
println(s.isInstanceOf[(Int,Int)=>Int])
}
}
使用场景如下
class Student(val name:String) {
def map(f:Student=>String):String={
return f(this)
}
}
object Student{
def apply(name: String): Student = new Student(name)
def main(args: Array[String]): Unit = {
val stu=Student("张三")
var str=stu.map(x=>x.name)
println(str)
}
}
分析:
复杂的函数对象编程
class Student(val name:String) {
def map1(f:Student=>String):String={
return f(this)
}
val map2:(Student => String) => String = (x:(Student=>String))=> x.apply(this)
}
object Student{
def apply(name: String): Student = new Student(name)
def main(args: Array[String]): Unit = {
var s:Student=Student("王五")
s.map1(x=>x.name)
s.map2(x=>x.name)
}
}
PartitalFunction(偏函数) -了解
偏函数主要适用于处理指定类型的据,通常用于集合处理中。
def main(args: Array[String]): Unit = {
Array(1,2,3,"a","b","c").collect(pf1).foreach(println)
}
//自定义偏函数
val pf1 = new PartialFunction[Any, Int] {
override def isDefinedAt(x: Any): Boolean = {
if(x.isInstanceOf[Int]) true else false
}
override def apply(v1: Any): Int = {
v1.asInstanceOf[Int]
}
}
//case 实现偏函数
val pf2 :PartialFunction[Any, Int]={
case x:Int => x+1
}
参考:https://blog.csdn.net/bluishglc/article/details/50995939
高阶函数
--->【摘自官方文档】
高阶函数将其他函数作为参数或作为结果返回函数。这是可能的,因为函数是Scala中的第一类值。这个术语在这一点上可能会有点混乱,我们对于将函数作为参数或返回函数的方法和函数使用短语“高阶函数”。
其中一个最常见的例子是mapScala中可用于集合的高阶函数。
val salaries = Seq(20000, 70000, 40000)
val doubleSalary = (x: Int) => x * 2
val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
doubleSalary是一个函数,它接受一个Int x,并返回x * 2。通常,箭头左侧的元组=>是参数列表,右侧表达式的值是返回的值。在第3行,该函数doubleSalary应用于工资列表中的每个元素。
要缩小代码,我们可以使函数匿名并将其作为参数直接传递给map:
val salaries = Seq(20000, 70000, 40000)
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
注意x在上面的例子中如何不声明为Int。那是因为编译器可以根据函数映射的类型推断出类型。编写同一段代码的更为惯用的方法是:
val salaries = Seq(20000, 70000, 40000)
val newSalaries = salaries.map(_ * 2)
由于Scala编译器已经知道参数的类型(单个Int),因此您只需要提供函数的右侧。唯一需要注意的是,您需要使用_代替参数名称(它x在前面的示例中)。
- 将方法强制转换为函数
也可以将方法作为参数传递给高阶函数,因为Scala编译器会将该方法强制转换为函数。
case class WeeklyWeatherForecast(temperatures: Seq[Double]) {
private def convertCtoF(temp: Double) = temp * 1.8 + 32
def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
}
这里将方法convertCtoF传递给forecastInFahrenheit。这是可能的,因为编译器强制convertCtoF执行该函数x => convertCtoF(x)(注意:x将是一个生成的名称,保证在其范围内是唯一的)。
- 接受函数的函数
使用高阶函数的一个原因是减少冗余代码。假设您想要一些可以通过各种因素提高某人工资的方法。如果不创建高阶函数,它可能看起来像这样:
object SalaryRaiser {
def smallPromotion(salaries: List[Double]): List[Double] =
salaries.map(salary => salary * 1.1)
def greatPromotion(salaries: List[Double]): List[Double] =
salaries.map(salary => salary * math.log(salary))
def hugePromotion(salaries: List[Double]): List[Double] =
salaries.map(salary => salary * salary)
}
请注意三种方法中的每一种方法如何仅因乘法因子而异。为简化起见,您可以将重复的代码提取到更高阶的函数中,如下所示:
object SalaryRaiser {
private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] =
salaries.map(promotionFunction)
def smallPromotion(salaries: List[Double]): List[Double] =
promotion(salaries, salary => salary * 1.1)
def bigPromotion(salaries: List[Double]): List[Double] =
promotion(salaries, salary => salary * math.log(salary))
def hugePromotion(salaries: List[Double]): List[Double] =
promotion(salaries, salary => salary * salary)
}
新方法promotion采用工资加上类型函数Double => Double (即采用Double并返回Double的函数)并返回产品。
- 返回函数的函数
在某些情况下,您要生成一个函数。这是一个返回函数的方法示例。
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
val schema = if (ssl) "https://" else "http://"
(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
}
val domainName = "www.example.com"
def getURL = urlBuilder(ssl=true, domainName)
val endpoint = "users"
val query = "id=1"
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
注意urlBuilder的返回类型(String, String) => String。这意味着返回的匿名函数需要两个字符串并返回一个String。在这种情况下,返回的匿名函数是(endpoint: String, query: String) => s"https://www.example.com/$endpoint?$query"。
隐式传值\隐式转换
-
隐式值获取
scala> implicit val n=1 //声明隐式值 scala> val b=implicitly[Int] b: Int = 1
def main(args: Array[String]): Unit = { implicit var str="张三" //声明隐式值 val b=implicitly[String] println(b+"你好") //结果:张三你好 }
使用implicitly[类型],必须保证当前上下文有且仅有一个隐式值类型,一般这种隐式值变量的声明写在object单例类或者伴生对象中。
- 隐式注入值
要求参数之中只能有一个implicit类型匹配。
implicit val s="您好"
def sayHi1(implicit msg:String):Unit={
println(msg)
}
def sayHi2(name:String)(implicit msg:String):Unit={//柯利化写法,要求必须作为最后一个参数传入
println(msg+" "+name)
}
def sayHi3(implicit name:String,msg:String):Unit={//标准函数写法,要求implict修饰的隐式值,必须放在第一位
println(msg+" "+name)
}
sayHi1
sayHi2("张三")
sayHi2("李四")("早上好")
sayHi3(implicitly[String],"hello") //不常见
错误写法:
def sayHi4(msg:String,implicit name:String):Unit={
println(msg+" "+name)
}
def sayHi4(implicit name:String)(msg:String):Unit={
println(msg+" "+name)
}
如果是标准函数写法,要求implict修饰的隐式值,必须放在第一位
如果是柯利化写法,要求必须作为最后一个参数传入
-
参数隐式转换
def main(args: Array[String]): Unit = { implicit def str2stu(v:String):Student={ new Student(v) } sayHi2Stu("王五") } def sayHi2Stu(stu:Student):Unit={ println("学生:"+stu.name+" 你好!") }
-
隐式方法加强
class Student(val name:String) { def study():Unit={ println("学生:"+name+" 学习..") } } object UserImplicts{ implicit class StudentImplicts(stu:Student){//参数给个对象就能给该对象加上该对象不存在的方法 def playGame():Unit={ println("学生 "+stu.name+" 玩游戏") } } } object Student{ import UserImplicts._ def main(args: Array[String]): Unit = { val s=new Student("张三") s.study s.playGame } }
隐式加强,优先调用对象自己的方法,如果该调用对象本身没有方法,会尝试查找编译的上下文,看当前上下文中是否有对应的隐式加强方法。
Scala 泛型
<:上边界限定(居多)
trait Keeper[U <: Animal]{
def keep(a:U);
}
表示Keeper的实现类只能饲养Animal以及Animal的子类
:下界限定
trait Keeper[U >: Dog]{
def keep(a:U);
}
表示Keeper的实现类只能饲养Dog或者Dog的父类
<%视图限定
implicit def str2Stu(v:String):Student={
new Student(v)
}
def getStu[T <% Student](v:T): T ={
return v
}
val stu=getStu("张三")
运行的时候尝试将T类型隐式转换为Student类型
T:A上下文绑定
必须满足A[T]隐式值
class Student[T]() {
def map(t:T):String={
t.toString +" world!"
}
}
def main(args: Array[String]): Unit = {
implicit var s=new Student[String]() //Student隐士值
say[String]("hello")
}
def say[T:Student](t:T):Unit={ //[T:Student]:类型参数 (t:T)值参数
var stu=implicitly[Student[T]]
stu.map(t)
}
def f[A:ClassTag](n: Int):Array[A] = {
return new Array[A](n)
}
上下文中,必须含有Student[String]隐式值,可以有效防止缺失参数,例如ClassTag该标签系统已经为 A:ClassTag创建了ClassTag[A]的隐式转换,而该隐式转换时在创建Array数组必须要用的。所以在给数组指定泛型的时候需要加ClassTag
多重界定符
-
A和B为T上界
def sayT <: A with B:Unit={ println(v) } trait A{} trait B{}
-
A和B为T下界
def sayT >: A with B:Unit={ println(v) } trait A{} trait B{}
-
同时拥有上界和下界,并且A为下界,B为上界,A为B的子类,顺序不能颠倒
def sayT >: A <: B:Unit={ println(v) } trait A{} trait B{}
-
视图界定,即同时能够满足隐式转换的A和隐式转换的B
def sayT <% A <% B:Unit={ println(v) } trait A{} trait B{}
+A 协变
如果范型里存在继承关系,则继承关系也是可以有多态的,子类范型赋值给父类范型,协变。
class Covariant[+T](t:T){}
val cov = new Covariant[Dog](new Dog("小狗"))
val cov2:Covariant[Animal] = cov
-A 逆变
class Covariant[-T](t:T){}
val cov = new Covariant[Animal](new Animal("动物"))
val cov2:Covariant[Dog] = cov
A 不变
class Covariant[T](t:T){}
val cov = new Covariant[Animal](new Animal("动物"))
val cov2:Covariant[Animal] = cov
Scala数组|集合
Array
var arr1=Array(1,2,3,4,5) //第一种创建方式 默认调用Array的apply方法
var arr2=new Array[Int](5)//第二种创建方式 指定泛型
println(arr1.apply(1))
arr1.foreach(i=>println(i))//数组遍历
Range区间
scala> var range=new Range(1,10,2)
range: scala.collection.immutable.Range = inexact Range 1 until 10 by 2
scala> var range=Range(1,10 ,2)
range: scala.collection.immutable.Range = inexact Range 1 until 10 by 2
scala> var range=0 to 10 by 2
range: scala.collection.immutable.Range = Range 0 to 10 by 2
scala> var range=0 until 10 by 2
range: scala.collection.immutable.Range = Range 0 until 10 by 2
向量
scala> var vector=Vector(1,2,3)
vector: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3)
scala> var vector=for(i<- 0 to 10 by 2;if(i>4)) yield i
vector: scala.collection.immutable.IndexedSeq[Int] = Vector( 6, 8, 10)
Iterator
只可以遍历一次
var it=Iterator(1,2,3,4,5,6)
it.foreach(x=>println(x)) //第一次遍历有结果
it.foreach(println) //第二次遍历没结果
List集合(不可变集合)
- :: 该方法被称为cons,意为构造,向队列的头部追加数据,创造新的列表。用法为 x::list,其中x为加入到头部的元素,无论x是列表与否,它都只将成为新生成列表的第一个元素,也就是说新生成的列表长度为list的长度+1(btw, x::list等价于list.::(x))
- :+和+: 两者的区别在于:+方法用于在尾部追加元素,+:方法用于在头部追加元素,和::很类似,但是::可以用于pattern match ,而+:则不行. 关于+:和:+,只要记住冒号永远靠近集合类型就OK了。
- ++ 该方法用于连接两个集合,list1++list2
- ::: 该方法只能用于连接两个List类型的集合
::/+:追加元素
var list =List(1,2,3,4,5)
list::=6 //默认追加到集合的最前面
list+:=8 //默认追加到集合的最前面
list.foreach(println) //6,1,2,3,4,5
list(0)=10 //报错 list不允许修改
println(list(0)) //获取第一个元素
:::合并集合
var list1=List(1,2,3)
var list2=List(4,5,6)
list1:::=list2
list1.foreach(println) //把list2合并追加到list1前面得到新的集合{4,5,6,1,2,3}
list2.foreach(println) //4,5,6 list2结果不变
drop
var list2=List(4,5,6,7,8)
val ints = list2.drop(2) //删除前两个元素返回一个新集合ints
ints.foreach(println) //6,7,8
list2.foreach(println) //原来集合元素不变 结果为:4,5,6,7,8
slice 截取某个区间的数据
var list=List(1,2,3,4,5)
list.slice(1,3).foreach(println)//结果为2,3-->返回(1,3]不包括1,包括3的新集合
reverse(翻转)
var list=List(1,2,3,4,5)
list.reverse.foreach(println) //结果为:5,4,3,2,1
take/takeRight
var list=List(1,2,3,4,5)
list.take(3).foreach(println)//获取集合前三个元素--> 1,2,3
list.takeRight(3).foreach(println)//获取集合后三个元素--> 3,4,5
takeWhile
从集合头部看来是匹配,遇到第一个false则终止
var list=List(1,2,3,4,5)
list.takeWhile(x=> x<4).foreach(println) //从头匹配,遇到第一个false则终止--> 1,2,3
head
获取集合中第一个元素
var list=List(1,2,3,4,5)
println(list.head) //获取集合中第一个元素-->结果为:1
tail
获取集合中除去第一个元素之外的所有元素
var list=List(1,2,3,4,5)
println(list.tail)//获取集合中除去第一个元素之外的所有元素 -->结果为:(2,3,4,5)
ListBuffer(可变集合)
def main(args: Array[String]): Unit = {
var list=ListBuffer[Int]()
list.+=(1)
list.+=(2,3,4,5) //ListBuffer是可变的集合
list.update(1,6) //将下标1的元素修改为6
list.remove(0) //删除指定下表的元素
list.insert(0,7) //在指定位置添加元素
list.insertAll(0,List(11,12,13)) //在指定位置插入一个集合
list++=(List(21,22,23)) //在集合后面追加一个集合
list.+=(24) //在集合最后追加一个元素
list.-=(23) //在几何中删除指定元素
list--=List(1,2,3) //在集合中删除一个集合
println(list.size) //返回集合的长度
println(list.length) //返回集合的长度
println(list.sum) //返回集合中所有元素的和
list.foreach(println)
}
Set(不可变)
scala.collection.immutable.Set
scala> var s=Set(1,2,3)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> s += 4
res88: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)
scala> s++=(List(4,5,6))
res89: scala.collection.immutable.Set[Int] = Set(1, 5, 2, 6, 3, 4)
scala> s-=(3)
res90: scala.collection.immutable.Set[Int] = Set(1, 5, 2, 6, 4)
scala> s.size
res91: Int = 5
scala> s.sum
res92: Int = 18
scala> s.ma
map mapResult max maxBy
scala> s.max
res93: Int = 6
scala> s.min
res94: Int = 1
Set(可变)
scala.collection.mutable.Set
scala> var s=scala.collection.mutable.Set(1,2,3)
s: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
scala> s += 4
res99: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4)
scala> s++=(List(4,5,6))
res100: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 6, 3, 4)
scala> s-=(3)
res101: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 6, 4)
scala> s.remove(6)
res102: Boolean = true
scala> s.add(9)
res103: Boolean = true
scala> s.size
res104: Int = 5
scala> s.sum
res105: Int = 21
scala> s.min
res106: Int = 1
scala> s.max
res107: Int = 9
HashMap(不可变)
scala.collection.immutable.HashMap
scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)
scala> hm+="工商"->"003" //等价hm+=("工商","003")
scala> hm-=("工商")
scala> val value:Option[String] = hm.get("建设")
value: Option[String] = Some(001)
scala> println(value.getOrElse(""))
001
scala>
scala> hm.size
res3: Int = 2
scala> for(i<- hm) println(i._1+" -> "+i._2)
建设 -> 001
招商 -> 002
scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)
scala> hm+="工商"->"003"
scala> hm-=("工商")
scala> //高阶函数
scala> var mergerFunction:((String,String),(String,String))=>(String,String)=(tuple1,tuple2)=>{
| println(tuple1+" -> "+tuple2)
| if(tuple1._1.equals(tuple2._1)){
| (tuple1._1,tuple2._2)
| }else{
| tuple1
| }
| }
mergerFunction: ((String, String), (String, String)) => (String, String) = $$Lambda$1152/2052572633@742aa00a
scala> var newhm=HashMap[String,String](("邮政","004"))
newhm: scala.collection.immutable.HashMap[String,String] = Map(邮政 -> 004)
scala> var res=hm.merged(newhm)(mergerFunction)
res: scala.collection.immutable.HashMap[String,String] = Map(邮政 -> 004, 建设 -> 001, 招商 -> 002)
scala> for(i<- res) println(i)
(邮政,004)
(建设,001)
(招商,002)
HashMap(可变)
scala.collection.mutable.HashMap
import scala.collection.mutable
object TestHashMap {
def main(args: Array[String]): Unit = {
var map=mutable.HashMap[String,String](("001","Lijb"),("002","Houy"))
map+="003"->"Donghy" //添加一个元组/键值对
map-="003" //根据健删除键值对
map.put("004","Gex") //添加一个元组/键值对
map.remove("004") //根据健删除键值对
//遍历方式
map.foreach(t=>println(t._1+"->"+t._2))
map.foreach(println)
for(i<- map) println(i)
}
}
数组集合常用方法
排 序
sorted sortBy() sortWith()
def main(args: Array[String]): Unit = {
var arr=Array (8,7,3,4,5)
arr.sorted.foreach(println)//升序排序
var arr1=Array(("a",1),("c",2),("b",3),("d",5))
arr1.sortBy(t=>t._1) //等价list.sortBy(_._1)
arr1.sortWith((t1,t2)=>t1._2
数组展开
flatten
def main(args: Array[String]): Unit = {
var list=List(Array("a","c"),Array("d","b"))
list.flatten.foreach(println) //a,c,d,b
}
转换
map方法将一个函数应用到某个集合的所有元素并返回结果;
foreach将函数应用到每个元素。
scala> var lst=List("hello world","good good study","day day up")
lst: List[String] = List(hello world, good good study, day day up)
scala> lst.map(item=>item.split(" "))
res15: List[Array[String]] = List(Array(hello, world), Array(good, good, study), Array(day, day, up))
scala>lst.map(item=>item.split(" ")).flatten
res16: List[String] = List(hello, world, good, good, study, day, day, up)
转换并展开
flatMap
scala> lst.flatMap(item=>item.split(" "))
res19: List[String] = List(hello, world, good, good, study, day, day, up)
scala> lst.flatMap(_.split(" "))
res20: List[String] = List(hello, world, good, good, study, day, day, up)
过滤
filter方法输出所有匹配某个特定条件的元素:
scala> var lst=List("hello world","good good study","day day up")
lst: List[String] = List(hello world, good good study, day day up)
scala> lst.flatMap(_.split(" ")).filter(_.equals("good"))
res22: List[String] = List(good, good)
元组排序
scala> var lst=Array(("a",1),("c",4),("b",3),("d",2))
lst: Array[(String, Int)] = Array((a,1), (c,4), (b,3), (d,2))
scala> lst.sorted
res9: Array[(String, Int)] = Array((a,1), (b,3), (c,4), (d,2))
scala> lst.sortBy(x=>x._2)
res11: Array[(String, Int)] = Array((a,1), (d,2), (b,3), (c,4))
定制排序规则
scala> var lst=Array(("a",1),("c",4),("b",3),("d",2))
lst: Array[(String, Int)] = Array((a,1), (c,4), (b,3), (d,2))
scala> lst.sortWith((x,y)=> {x._1>y._1} )
res12: Array[(String, Int)] = Array((d,2), (c,4), (b,3), (a,1))
分组统计
scala> var lst=Array("a","b","d","c","b","a","d","d")
lst: Array[String] = Array(a, b, d, c, b, a, d, d)
scala> lst.groupBy(x=>x)
res51: scala.collection.immutable.Map[String,Array[String]] = Map(b -> Array(b,
b), d -> Array(d, d, d), a -> Array(a, a), c -> Array(c))
scala> var lst=Array(("a",1),("b",2),("a",2))
lst.groupBy(x=>x._1).map(x=>(x._1,(for(i <- x._2) yield i._2).sum))
fold统计
var lst=Array(Array(1,2,3),Array(1,2,4),Array(5,6,7),Array(9,4))
lst.flatten.fold(0)((x,y)=>x+y)//结果44 所有元素的和
aggregate
scala> var lst=List(1,2,4,5,6)
lst: List[Int] = List(1, 2, 4, 5, 6)
初始值 局部计算 汇总逻辑
scala> lst.aggregate(0)((x,y)=>x+y,(x,y)=>x+y) //等价 lst.aggregate(2)(_+_,_+_)
res11: Int = 18
Reduce
scala> var lst=List(1,2,4,5,6)
lst: List[Int] = List(1, 2, 4, 5, 6)
scala> lst.reduce(_+_)
res12: Int = 18
group
cala> var lst=Array(1,2,4,5,6,7)
lst: Array[Int] = Array(1, 2, 4, 5, 6, 7)
scala> lst.grouped(3).toList //将数组按给定长度分成若干分数组并且组合成一个集合
res18: List[Array[Int]] = List(Array(1, 2, 4), Array(5, 6, 7))
zip
scala> var v=Vector(1,2,4)
v: scala.collection.immutable.Vector[Int] = Vector(1, 2, 4)
scala> v.zip(Array("a","b","c"))
res22: scala.collection.immutable.Vector[(Int, String)] = Vector((1,a), (2,b), (4,c))
unizp
scala> var v=List(("a",1),("b",2),("c",3))
v: List[(String, Int)] = List((a,1), (b,2), (c,3))
scala> v.unzip
res24: (List[String], List[Int]) = (List(a, b, c),List(1, 2, 3))
diff | intersect | union
scala> var v=List(1,2,3)
v: List[Int] = List(1, 2, 3)
scala> v.diff(List(2,3,5)) //diff求并集
res27: List[Int] = List(1)
scala> var v=List(1,2,3,5)
v: List[Int] = List(1, 2, 3, 5)
scala> v.intersect(List(2,4,6)) //intersert求交集
res37: List[Int] = List(2)
scala> var v=List(1,2,3,5)
v: List[Int] = List(1, 2, 3, 5)
scala> v.union(List(2,4,6)) //两集合进行合并
res0: List[Int] = List(1, 2, 3, 5, 2, 4, 6)
distinct
scala> var v=List(1,2,3,3,5)
v: List[Int] = List(1, 2, 3, 3, 5)
scala> v.distinct //去重
res29: List[Int] = List(1, 2, 3, 5)
Sliding
scala> var v=List(1,2,3,3,5)
v: List[Int] = List(1, 2, 3, 3, 5)
scala> v.sliding(2,1).toList //交叉一个元素,每个集合两个元素
res35: List[List[Int]] = List(List(1, 2), List(2, 3), List(3, 3), List(3, 5))
数组集合方法的应用场景
字符统计
def main(args: Array[String]): Unit = {
var arrs=Array("this is a Statistical demo","good good study","day day up")
arrs.map(_.split(" ")) // 按""分割
.flatten //将元素展开
.map((_,1)) //将集合中的元素统计+1
.groupBy(_._1)//按照每一个元组的第一个元素(也就是不重复的每一个单词)分类
.map(x=>(x._1,(for(i<- x._2) yield i._2.toInt)//遍历map,并且遍历map里的值算其长度,并提取出来
.toList.sum))//进行相加统计
.toList
.sortBy(_._1)
.foreach(println)
}
文件字符统计
def main(args: Array[String]): Unit = {
var source=Source.fromFile("D:\\t_word.txt")
var array=ArrayBuffer[String]();
val breaks=Breaks
breaks.breakable({
val reader = source.bufferedReader()
while(true){
val line = reader.readLine()
if(line==null){
breaks.break()
}
array+=line
}
})
array.flatMap(_.split(" "))
.map((_,1))
.groupBy(_._1)
.map(x=>(x._1,x._2.size))
.toList
.sortBy(_._1)
. foreach(println)
}