Scala学习-变量常量、运算符、流程控制和函数

断了今生、忘了曾经 提交于 2020-02-17 18:58:44

scala是马丁.奥德斯克设计的,专门为程序员设计,广泛应用于大数据的语言。它同时支持面向对象和面向函数编程,运行scala需基于JVM,使用它需要提前安装好JDK和scala SDK。scala的的代码一行可以顶多行java代码,开发效率高,并且兼容java类库,scala编译完也是.class文件。另外大数据框架kafka和spark是基于scala开发的,因此想做流处理需要学习scala。

基本数据类型

scala中没有基本类型的说法,绝大多数类型(类似java类型)都封装成了类,以下是它的简图。

(1)Any是scala顶级父类。

(2)AnyVal是所有数值类型的父类,注意有一个Unit,它只有一个实例()。

(3)AnyRef是所有对象类型的父类,注意Null是它的子类,它的实例对象是null,可以赋值给任意对象类型。

(3)Nothing可以是任何类型的子类,可以表示不正常的返回值类型,如异常。

# RPEL交互方式
scala> def test():Nothing={
     | throw new IllegalArgumentException()
     | }
test: ()Nothing

变量和常量

变量声明的语法:'var 变量名:数据类型=数据值' 或者 'var 变量名=数据值',后者会进行自动类型转换。常量声明类似,使用val代表常量。

使用变量或常量需要标识符的支撑,scala中也有一套标识符规则。

标识符规则

(1)使用数字、字母、下划线和$符号;

scala> var name:String="clyang"
name: String = clyang

scala> var age:Int=28
age: Int = 28

(2)不能以数字开头;

(3)如果有特殊字符,标识符全部都需要是特殊字符;

scala> var +-*/ :Double=3.14
+-*/: Double = 3.14

(4)可以使用``来标记任何字符;

# 可以用scala的关键字,任何字符均可
scala> var `private`:String="test"
private: String = test

(5)见名知意;

# year代表年,365天
scala> val year:Int=365
year: Int = 365

# 如果是常量,声明后不可以修改
scala> year=3650
<console>:13: error: reassignment to val
       year=3650
           ^

类型转换

(1)自动类型推导

# 自动识别类型
scala> var name="clyang"
name: String = clyang

scala> var age=28
age: Int = 28

(2)手动指定向上类型,否则按自动类型推导出数据类型

# 默认是Double类型,手动指定Any类型
scala> var a:Any=3.14
a: Any = 3.14

(3)强制转换,从大往小转

# 强制Double转Int
scala> var d:Double=3.14
d: Double = 3.14

scala> var i:Int=d.toInt
i: Int = 3

(4)自定义类型转换,需自定义函数,可以显示调用和隐式调用。

显示调用

# 自定义类型转换函数
scala> def string2Int(str:String):Int={
     | return Integer.parseInt(str)
     | }
string2Int: (str: String)Int
# 给一个字符串
scala> var str="268"
str: String = 268
# 调用转换
scala> var number=string2Int(str)
number: Int = 268

隐式调用

# 需使用implicit关键字
scala> implicit def string2Int(str:String):Int={
     | return Integer.parseInt(str)
     | }
warning: there was one feature warning; re-run with -feature for details
string2Int: (str: String)Int

scala> str
res1: String = 268

scala> var number:Int=str
number: Int = 268

(5)向上造型创建的对象,可以使用asInstanceOf来向下转。

# 定义Person类
scala> class Person{}
defined class Person

# 创建Any类型的对象,向上造型
scala> var p:Any=new Person()
p: Any = Person@6a370f4

# 向下转
scala> var s:Person=p.asInstanceOf[Person]
s: Person = Person@6a370f4

运算符

scala中运算符包括算术、赋值、关系、逻辑和位运算,注意没有三元运算,并且所有的运算符都封装成了方法。

参考菜鸟教程:https://www.runoob.com/scala/scala-operators.html

算术

注意一下,scala中的运算符是封装成了方法,因此a+b,其实就是a.+(b)的简写,其他依次类推。

scala> var a=500
a: Int = 500

scala> var b=20
b: Int = 20

# 加
scala> a+b
res2: Int = 520

# 完整写法
scala> a.+(b)
res3: Int = 520

# 以下三种方式等效,都可以加1
scala> a=a+1
a: Int = 501

scala> a+=1

scala> a
res5: Int = 502

scala> a.+=(1)

scala> a
res7: Int = 503

如果有点,则计算点。

//运算符运用,有点先算点
println(3+5*7)//38
println(3+(5)*7)//38
println(7*3.+(5)) //56
println(7*3+5) //26
println(7*(3).+(5)) //56
println(3.+(5).*(7))//56

赋值

scala> var a=520
a: Int = 520

scala> var b=1314
b: Int = 1314

# 赋值运算的返回值类型为Unit,对应实例对象为()
scala> var z:Unit=a=b
z: Unit = ()

# a被赋值为1314
scala> a
res8: Int = 1314

关系

就是类似java中的大于小于等于之类的。

scala> var r=1314>520
r: Boolean = true
scala> var r="婚姻"=="车子房子票子"
r: Boolean = false

逻辑

逻辑与、逻辑或和逻辑非。

scala> var r=true&&true
r: Boolean = true

scala> var r=true&&false
r: Boolean = false

位运算

类似java中的位运算符,按位与、按位或、按位异或、取反、左移、右移、无符号右移。

//判断一个数是否是2的幂
println(8&7)//0
//左移比直接相乘快
println(4<<2)//16
println(~4)//-5 取反减1

流程控制

scala中没有switch-case,有条件判断和循环。

if条件判断

类似java,scala中if-else是有返回值结果的,根据if-else的{}最后一行来确认。

package day01.clyang.control

import scala.io.StdIn

/**
  * 流程控制
  */
object IfDemo2 {

  def main(args: Array[String]): Unit = {
    //if-else是有返回值结果的,根据if-else的{}最后一行来确认
    var i=StdIn.readInt()
    var result:String=if(i%2!=0){
      println("~~~")
      "我是谁我在哪里"
      "奇数"//最后一行
    }else{
      "偶数"//最后一行
    }
    //打印result
    println(result)
  }

}

控制台输入奇数后,只返回最后一行'奇数'。

99
~~~
奇数

scala中也支持if-else if。

package day01.clyang.control

import scala.io.StdIn

/**
  * 多级if-else
  */
object IfDemo3 {
  def main(args: Array[String]): Unit = {
    var score=StdIn.readInt()
    var result:String=if(score>90) "优秀"
    else if(score>80) "良好"
    else if(score>=70) "一般"
    else "及格"
    //打印结果
    println(result)
  }
}

控制台输入测试,ok。

98
优秀

循环

可以使用while循环,和for循环。

(1)while循环

# while循环,返回值类型为Unit
scala> var count=0
count: Int = 0

scala> var result=while(count<3){
     | println("while")
     | count=count+1
     | }
while
while
while
result: Unit = ()

(2)do-while循环,这个用的少

# do-while返回值类型也是Unit
scala> var count=0
count: Int = 0

# scala中,也类似python中,字符串可以和数字相乘
scala> var result=do{
     | println("*"*5)
     | count=count+1
     | }while(count<3)
*****
*****
*****
result: Unit = ()

(3)for循环

注意to和until的使用,两者均是函数。

# 准备一个数组
scala> var arr:Array[Int]=Array[Int](1,2,3,4,5)
arr: Array[Int] = Array(1, 2, 3, 4, 5)
# to为后包括
scala> var result=for(i <- 0 to arr.length-1){
     | println(arr(i))
     | }
1
2
3
4
5
result: Unit = ()
# until为后不包括
scala> var result=for(i <- 0 until arr.length){
     | println(arr(i))
     | }
1
2
3
4
5
result: Unit = ()

to和until都是函数,如果要指定步长,就不能省略.和( )。

# 指定步长,打印0到10范围的偶数
scala> for(i <- 0.to(10,2)){
     | println(i)
     | }
0
2
4
6
8
10
# 倒着来
scala> for(i <- 10.to(0,-2)){
     | println(i)
     | }
10
8
6
4
2
0
# reverse倒着来
scala> for(i <- 0 to 3 reverse){
     | println(i)
     | }
warning: there was one feature warning; re-run with -feature for details
3
2
1
0

for循环最后一行的结果,可以通过yield关键字把它放到一个集合,集合类型是Vector。

scala> import scala.io.StdIn
import scala.io.StdIn
# 输入5
scala> var n=StdIn.readInt()
n: Int = 5
# 返回集合类型为Vector
scala> var result=for(i <- 0 to n) yield i*i
result: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 4, 9, 16, 25)

for循环中,如果嵌套了if判断,就构成了守卫循环。

# 打印100以内5的倍数,但是不是7的倍数
scala> for(i <- 0.to(100,5)){
     | if(i%7!=0){
     | println(i)
     | }
     | }
5
10
15
20
25
30
40
45
50
55
60
65
75
80
85
90
95
100
# 可以使用守卫循环
scala> for(i <- 0.to(100,5) if(i%7!=0)){
     | println(i)
     | }
5
10
15
20
25
30
40
45
50
55
60
65
75
80
85
90
95
100

for循环嵌套,scala中还有大括号写法。

# 普通写法
scala> for(i <- 1 to 9){
     |   for(j <- 1 to i){
     |   print(i+"*"+j+"="+i*j+"\t")
     |   }
     | println()
     | }
1*1=1
2*1=2   2*2=4
3*1=3   3*2=6   3*3=9
4*1=4   4*2=8   4*3=12  4*4=16
5*1=5   5*2=10  5*3=15  5*4=20  5*5=25
6*1=6   6*2=12  6*3=18  6*4=24  6*5=30  6*6=36
7*1=7   7*2=14  7*3=21  7*4=28  7*5=35  7*6=42  7*7=49
8*1=8   8*2=16  8*3=24  8*4=32  8*5=40  8*6=48  8*7=56  8*8=64
9*1=9   9*2=18  9*3=27  9*4=36  9*5=45  9*6=54  9*7=63  9*8=72  9*9=81
# 两个循环嵌套到一起,scala中不推荐使用分号
scala> for(i <- 1 to 9; j <- 1 to i){
     | print(i+"*"+j+"="+i*j+"\t")
     | if(i==j) println()
     | }
1*1=1
2*1=2   2*2=4
3*1=3   3*2=6   3*3=9
4*1=4   4*2=8   4*3=12  4*4=16
5*1=5   5*2=10  5*3=15  5*4=20  5*5=25
6*1=6   6*2=12  6*3=18  6*4=24  6*5=30  6*6=36
7*1=7   7*2=14  7*3=21  7*4=28  7*5=35  7*6=42  7*7=49
8*1=8   8*2=16  8*3=24  8*4=32  8*5=40  8*6=48  8*7=56  8*8=64
9*1=9   9*2=18  9*3=27  9*4=36  9*5=45  9*6=54  9*7=63  9*8=72  9*9=81
# 大括号写法
scala> for{
     | i <- 1 to 9
     | j <- 1 to i
     | }
     | {
     | print(i+"*"+j+"="+i*j+"\t")
     | if(i==j){
     |   println()
     |   }
     | }
1*1=1
2*1=2   2*2=4
3*1=3   3*2=6   3*3=9
4*1=4   4*2=8   4*3=12  4*4=16
5*1=5   5*2=10  5*3=15  5*4=20  5*5=25
6*1=6   6*2=12  6*3=18  6*4=24  6*5=30  6*6=36
7*1=7   7*2=14  7*3=21  7*4=28  7*5=35  7*6=42  7*7=49
8*1=8   8*2=16  8*3=24  8*4=32  8*5=40  8*6=48  8*7=56  8*8=64
9*1=9   9*2=18  9*3=27  9*4=36  9*5=45  9*6=54  9*7=63  9*8=72  9*9=81

Break

scala中,没有break和continue关键字,如果要退出循环,可以使用Break类来跳出循环。

(1)跳出循环,报异常,并且for循环后面语句不执行。

for(i <- 0.to(50,5)){
      if(i==35) Breaks.break()
      println("我今年"+i+"岁,我依然很帅")
    }
//测试是否执行后面语句
println("finished~~")

# console显示没有打印
我今年0岁,我依然很帅
我今年5岁,我依然很帅
我今年10岁,我依然很帅
我今年15岁,我依然很帅
我今年20岁,我依然很帅
我今年25岁,我依然很帅
我今年30岁,我依然很帅
Exception in thread "main" scala.util.control.BreakControl

如果按以下这么写,即使跳出循环,依然执行for循环后面的语句。

Breaks.breakable(
  for(i <- 0.to(50,5)){
    if(i==35) Breaks.break() 
    println("我今年"+i+"岁,我依然很帅")
  }
)
//测试是否执行后面语句
println("finished~~")

# console显示打印了后面的语句
我今年0岁,我依然很帅
我今年5岁,我依然很帅
我今年10岁,我依然很帅
我今年15岁,我依然很帅
我今年20岁,我依然很帅
我今年25岁,我依然很帅
我今年30岁,我依然很帅
finished~~

# import scala.util.control.Breaks._后,这么写也行
breakable(
  for(i <- 0.to(50,5)){
    if(i==35) break()
    println("我今年"+i+"岁,我依然很帅")
  }
)

println("finished~~")

函数

函数在scala中地位非常高,是"一等公民",它可以作为函数的参数,也可以作为函数的返回值,并且可以定义在任何地方,函数定义的基本语法如下。

  /*
   * 函数的格式
   * def 函数名(参数列表):返回值类型={
   *   函数体
   *   return 返回值
   *   }
   */

入门使用

常规写法。

scala> def max(i:Int,j:Int):Int={
     | return if(i>j) i else j
     | }
max: (i: Int, j: Int)Int
# 调用
scala> max(1,3)
res0: Int = 3

如果没有return,默认将函数体最后一行的结果返回。

scala> def sum(i:Int,j:Int):Int={i+j}
sum: (i: Int, j: Int)Int

scala> sum(1,3)
res1: Int = 4

如果函数体只有一句,大括号可以省略,如果可以根据最后一行推导出返回值类型,方法返回值类型也可以省略。

scala> def sum(i:Int,j:Int)=i+j
sum: (i: Int, j: Int)Int

scala> sum(1,3)
res2: Int = 4

如果没有指定方法的返回值类型,方法体不能用return,根据函数体最后一行来推导。

scala> def sum(i:Int,j:Int)={return i+j}
<console>:10: error: method sum has return statement; needs result type
       def sum(i:Int,j:Int)={return i+j}
                             ^

方法如果没有返回值类型,可以使用Unit作为返回值类型。

scala> def printShape(row:Int,col:Int):Unit={
     | for(i <- 1 to row)
     |   println("*"*col)
     | }
printShape: (row: Int, col: Int)Unit

scala> printShape(3,3)
***
***
***

如果没有写返回值类型,也没写等号,则默认返回值类型为Unit。

scala> def printTest(){
     | println("this is return Unit")
     | }
printTest: ()Unit
# 如果函数没有参数,可以省略括号
scala> printTest
this is return Unit

函数进阶

scala可以直接调用java api。

scala> def getRandom()=(Math.random()*11).toInt
getRandom: ()Int
# 调用时加括号
scala> getRandom()
res7: Int = 0
# 没有参数,调用时括号可以省略
scala> getRandom
res8: Int = 10
# 方法没有参数,方法括号也可以省略
scala> def getRandom=(Math.random()*11).toInt
getRandom: Int
scala> getRandom
res9: Int = 3

scala中函数可以重载,类似java,根据方法签名来绑定执行的方法。

//scala中函数可以重载
def square(x:Int,y:Int)=x*y
def square(r:Int)=3.14*r*r

//根据参数不同,重载,好像一次只能调用一个,同时调用两个也只显示一个的结果

函数定义时如果没有(),调用时就不能加()。

scala> def test="this is test"
test: String
# 正常调用
scala> test
res15: String = this is test
# 加括号调用就报错
scala> test()
<console>:12: error: not enough arguments for method apply: (index: Int)Char in class StringOps.
Unspecified value parameter index.
       test()
           ^

高阶函数

将函数作为参数,或者函数的返回值也是函数,这样的函数叫做高阶函数,前面的闭包和柯理化就是高阶函数,返回值类型是函数。

(1)闭包

函数中嵌套函数,可以延长变量的生命周期,这是函数的闭包。

# 定义函数,打印最小值,如果有必要可以获取两个较大值的和
# 返回值为函数
scala> def min(x:Int,y:Int,z:Int)={
     | var min=x
     | if(y<min) min=y
     | if(z<min) min=z
     | println("最小值为:"+min)
     |   def sum(){ # 内部定义函数,返回值类型为Unit
     |   println(x+y+z-min) 
     |   }
     | sum _ # 将sum一整个函数作为返回值,调用它才执行sum方法,不调用就不执行
     | }
min: (x: Int, y: Int, z: Int)() => Unit
# 打印最小值,返回值类型是一个函数,() => Unit就是min方法返回值类型
scala> min(1,2,3)
最小值为:1
res17: () => Unit = <function0>
# 调用sum方法
scala> min(1,2,3)()
最小值为:1
5

min函数执行完后,会移除出栈内存,如果将参数也移除,那接下来如果想调用sum方法就没有了参数,在scala中,只会将min函数移除出栈内存,而参数x,y,z会保留,供sum函数使用,这样延长了x,y,z变量的生命周期,这样使用函数嵌套延长变量生命周期的方式叫做函数的闭包。

闭包的应用练习如下,求两个数的积,如果有必要添加第三个数来求积。

scala> def product(x:Int,y:Int):Int => Int={
     | println("两个数的积为:"+x*y)
     |  def add(z:Int):Int={
     |  println("三个数的积为:"+x*y*z)
     |  x*y*z
     |  }
     | add _
     | }
product: (x: Int, y: Int)Int => Int

scala> product(3,4)
两个数的积为:12
res19: Int => Int = <function1>

scala> product(3,4)(5)
两个数的积为:12
三个数的积为:60
res20: Int = 60
(2)柯理化

柯理化是闭包的一种简化形式,写法更加简洁,但是使用没有闭包灵活,必须传入指定数目参数才能执行。

# 比较两个值的较大值
# 1 闭包写法
scala> def max(x:Int)={
     |  def max1(y:Int)={
     |  if (x>y) x else y
     |  }
     | max1 _
     | }
max: (x: Int)Int => Int

scala> max(1)
res21: Int => Int = <function1>

scala> max(1)(2)
res22: Int = 2
# 2 柯理化写法
scala> def max2(x:Int)(y:Int)={
     | if (x>y) x else y
     | }
max2: (x: Int)(y: Int)Int

scala> max2(1)(2)
res23: Int = 2
(3)函数作为参数

函数可以作为参数,这也是高阶函数的一种。

# 函数作为参数传入
scala> def printResult(x:Int,y:Int,f:(Int,Int)=>Int):Unit={
     | println(f(x,y))
     | }
printResult: (x: Int, y: Int, f: (Int, Int) => Int)Unit
# 定义函数1
scala> def sum(x:Int,y:Int)=x+y
sum: (x: Int, y: Int)Int
# 定义函数2
scala> def minus(x:Int,y:Int)=x-y
minus: (x: Int, y: Int)Int
# 函数作为参数传入,根据函数不同,得到的结果也不同
scala> printResult(3,4,sum)
7
scala> printResult(3,4,minus)
-1 

函数作为参数传入,可以直接将匿名函数传入刚才定义的函数。

scala> printResult(3,4,(x:Int,y:Int)=>x*y)
12

如果参数类型确定,可以省略类型。

scala> printResult(3,4,(x,y)=>x*y)
12

如果参数只在匿名函数方法体中出现一次,可以用下划线来代替。

scala> printResult(3,4,_*_)
12

函数的递归

类似java和python,scala中也可以使用递归,需要注意的是递归需要指定返回值类型,因为无法通过类型自动推导。

# 求阶乘
scala> def getfactorial(num:Int):Int={
     | if (num==1) return 1 //不写return,会报StackOverflowError,因为执行到此处,会依然往后执行,函数使用最后一行结果作为返回
     | return num*getfactorial(num-1)
     | }
getfactorial: (num: Int)Int

scala> getfactorial(5)
res33: Int = 120

可变参数

类似java中参数可以传入可变参数(使用...),scala中也可以实现,使用 " 变量类型* " 来表示任意多个可变参数。

# 求和,参数变长
scala> def sum(arr:Int*)={
     | var sum=0
     | for(i <- arr) sum+=i # 增强型for循环
     | sum
     | }
sum: (arr: Int*)Int
scala> sum(1,2,3,4,5,6,7,8,9,10)
res35: Int = 55

函数中可设定默认值

函数中可以给参数默认值,如果方法没有传入此参数就按默认值来计算。

scala> def getOffPrice(money:Double,offFactor:Double=1)=money*offFactor
getOffPrice: (money: Double, offFactor: Double)Double
# 按默认值计算
scala> getOffPrice(100)
res36: Double = 100.0
# 按给定值计算
scala> getOffPrice(100,0.8)
res37: Double = 80.0

懒值

scala中设置懒值,可以让懒值加载时才执行。

package boe.com.clyang.function

/**
 * 懒值
 * 加载时才真的运行
 */
object LazyValue {

  def sum(i:Int,j:Int)={
    println("sum is running~~~")
    i+j
  }

  def main(args: Array[String]): Unit = {
    var i=3
    var j=4
    //将sum求和设置为懒值
    lazy val result=sum(i,j)

    # 线程等待3秒,自动回来,模拟这里有很多的代码
    println("sleeping")
    Thread.sleep(3000)

    //只有这里真正加载时才执行sum求和
    println(result)

  }
}

控制台结果,可以看出虽然sum方法在写在前面,但是因为设置了懒加载,在等待3秒后,println打印时才真正加载执行。

sleeping
sum is running~~~
7

以上,是scala入门基础知识,记录一下备用。

参考资料:

(1)菜鸟教程

(2)scala官网

(3)https://blog.csdn.net/qq_31573519/article/details/82749188 守护循环

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!