2 控制结构和函数

文章目录

    • 条件表达式
    • 语句终止
    • 块表达式和赋值
    • 输入和输出
    • 循环
    • break和continue
    • 高级for循环
    • 函数
    • 默认参数和带名参数
    • 变长参数
    • 过程
    • 懒值
    • 异常

条件表达式

  • if/else语句同Java和C++一样
  • scala的if/else的表达式有值
if(x>0) 1 else -1
  • 将表达式的结果赋值给变量
val s = if(x>0) 1 else -1
  • scala中每个表达式都有一个类型,if(x>0) 1 else -1的类型是Int,因为两个分支的类型都是Int。对于混合类型表达式,分支的类型不一样,得到的类型是两个分支类型的超类型.
val s = if(x>0) 1 else "None"
// s: Any = 1
val s = if(x>0) 1 else -1.0
// s: Double = 1.0
//如果只有一个分支
val s = if(x>0) 1
//s: AnyVal = 1
val s = if(x>0) "a"
//s: Any = a
val s:String = if(x>0) "add"
//:12: error: type mismatch;
// found   : Unit
// required: String
//       val s:String = if(x>0) "add"
//                      ^

val s:String = if(x>0) "add" else "dfd"
// s: String = add
  • Unit类型,只有一个值,即一对小括号 ( ),括号里可以加空格,也可以不加
val a:Unit=(  )
// a: Unit = ()
a.getClass
// Class[Unit] = void
  • 交互式的REPL中输入多行代码,先输入:paste回车,输入内容,结束后按下Ctrl+D组合键结束键盘输入。

语句终止

  • scala的每一行结尾不需要加分号。
  • 如果一行写多条语句,每一条语句需要用分号分割。
  • 每一行想写分号也可以写上,it depends you

块表达式和赋值

  • 块语句,用大括号括起来的内容: { }
  • 块的最后一个表达式的值就是块的值
  • 赋值表达式的值是Unit类型,值的内容是()
 val k={3>2}
// k: Boolean = true
 val k={var a=1;var b=2}
// k: Unit = ()
  • 不要连续两次赋值,因为赋值表达式的值是Unit类型的,除非是确实想把Unit类型赋值给变量。
val y=x=1
// y: Unit = ()
x
//Int = 1
  • 也不要同时初始化三个变量赋值
val a=b=c=1  //错误
// :11: error: not found: value b
//       val a=b=c=1

输入和输出

  • 打印值用print或println。
  • println会自动在后面追加换行
  • C风格的printf函数
printf("Hello %s haha %d,%n","world",100)
// Hello world haha 100,
  • 插值字符串,包括f字符串和s字符串
  • f 字符串:被格式化的字符串以字母f开头,包含以$开头,并且可能带有 C风格的格式化字符串的表达式
val name = "Sam";val age =12
print(f"Hello,$name! In six months, you will be ${age + 0.5}%7.2f years old.%n")
//Hello,Sam! In six months, you will be   12.50 years old.
  • s 字符串:字符串可以包含表达式,但不能格式化。比f字符串少了格式化功能。
print(s"Hello,${"Bargins"}! In six months, you will be ${var age =93;age+0.5} years old.")
//Hello,Bargins! In six months, you will be 93.5 years old.
  • 从控制台输入scala.io.StdIn的readLine方法读取一行。此外还有读取特定类型的值。

readBoolean、 readByte 、 readChar 、 readDouble 、 readFloat 、 readInt readLine 、 readLong 、 readShort

循环

  • while和do循环同Java和C++一致
  • for循环与Java和C++不一致,for的结构是

for (i <- 表达式) {做些什么}

  • 例如1 到 10为 1 to 10 ,如果不包含后面的可以用 1 until 10字符串
for(i<-1 to 10){print(i)}
// 12345678910
val s="World"

for(i<-0 until s.length){print(s.charAt(i)+"  ,  ")}
// W  ,  o  ,  r  ,  l  ,  d  ,

for(i <- s){print(i+"  ,  ")}
// W  ,  o  ,  r  ,  l  ,  d  ,

break和continue

首先导入 import scala.util.control.Breaks._
break的功能

val array = Array(1,3,10,5,4)
breakable{
  for (i<- array){
    if (i>5) {break}
    println(i)
    println("haha")
  }
}

实现continue的功能

val array = Array(1,3,10,5,4)
for (i <- array){
  breakable {
    if (i > 5) break
    println(i)
  }
}

高级for循环

  • 生成器:变量<-表达式
for(i<-1 to 3;j<-1 to 3 ){print(f"${10*i+j}%3d")}
// 11 12 13 21 22 23 31 32 33
  • 守卫:每个生成器都可以带守卫,守卫和生成器之间没有分号
for(i<-1 to 3;j<-1 to 3 if i!=j ){print(f"${10*i+j}%3d")}
// 12 13 21 23 31 32
  • for推导式:for循环体的内容以yield开始
for(i<-1 to 3;j<-1 to 3 if i!=j ) yield 10*i+j
// scala.collection.immutable.IndexedSeq[Int] = Vector(12, 13, 21, 23, 31, 32)
//
var sum=0
for(i<-1 to 3;j<-1 to 3 if i!=j ) yield {sum+=10*i+j;sum}
// scala.collection.immutable.IndexedSeq[Int] = Vector(12, 25, 46, 69, 100, 132)

函数

  • 方法对对象进行操作,函数不是。
  • 书上的函数定义如下,但是其他资料看到,def定义的是方法,而函数是通过=>定义。这里不做严格的概念区分了,def的也算函数吧。
//方法
def abs(x:Double):Double=if(x>=0) x else -x
// abs: (x: Double)Double
// 函数
val absf=(x:Double)=>if(x>=0) x else -x
// absf: Double => Double = 
  • 在非函数式编程语言里,函数的定义包含了“函数类型”和“值”两种层面的内容。但是,在函数式编程中,函数是“头等公民”,可以像任何其他数据类型一样被传递和操作,也就是说,函数的使用方式和其他数据类型的使用方式完全一致了。这时,我们就可以像定义变量那样去定义一个函数,由此导致的结果是,函数也会和其他变量一样,开始有“值”。就像变量的“类型”和“值”是分开的两个概念一样,函数式编程中,函数的“类型”和“值”也成为两个分开的概念,函数的“值”,就是“函数字面量”。
    函数类型,输入参数的类型以及返回值的类型
  • 完整的函数的定义

val 函数名:( 输入参数类型 => 输出参数类型) = {(输入参数) => 函数体}

  • 其中函数的类型为( 输入参数类型 => 输出参数类型),函数的值为{(输入参数) => 函数体}
val sum:((Double,Double)=>Double)={(x,y)=>{x+y}}
// sum: (Double, Double) => Double = 

//省略返回类型
val sum1=(x:Double,y:Double)=>{x+y}
// sum1: (Double, Double) => Double = 

//继续化简,使用下划线
val sum2=(_:Double)+(_:Double)
sum2: (Double, Double) => Double = <function2>

//Triple
val triple=(_:Double)*3
// triple: Double => Double = 

//常用于高阶函数中,函数作为参数
Array(3,2,5).map(_*3)
// Array[Int] = Array(9, 6, 15)

默认参数和带名参数

  • 关键字参数
def add(a:Int,b:Int,c:Int)={s"$a,$b,$c"}
// add: (a: Int, b: Int, c: Int)String
add(2,1,4)
// String = 2,1,4
add(c=2,a=1,b=4)
// String = 1,4,2
  • 默认参数
def add2(a:Int,b:Int=10,c:Int=100)={s"$a,$b,$c"}
// (a: Int, b: Int, c: Int)String

add2(3)
// String = 3,10,100

add2(3,20)
// String = 3,20,100

add2(3,c=5,b=4)
//String = 3,4,5

变长参数

  • 参数数量不固定,参数类型为参数名:单个参数类型*
def sum(args:Int*)={
  var result=0
  for(arg<-args) result+=arg
  result
}
//
sum(2,3,4)
// Int = 9
sum(2,3,4,5,6)
// Int = 20
  • 将序列传入变长参数的函数,例如数组、列表等。参数为变量名、冒号、空格、下划线、星号
val arr=Array(2,3,4,5,6)
// arr: Array[Int] = Array(2, 3, 4, 5, 6)
sum(arr: _*)
// Int = 20
//参数中可以加入多个空格
sum(arr  :   _  *)
// Int = 20
// 其他序列
val arr2 = 1 to 10
// scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

sum(arr2: _*)
// Int = 55

过程

  • 函数体包含在花括号当中,但是没有前面的等号,那么返回类型就是Unit,这样的函数称作过程。过程不返回值,只为了他的副作用。
  • main函数一般是过程,不加等号
scala> def sum1(a:Int,b:Int)={a+b}
sum1: (a: Int, b: Int)Int

scala> def sum2(a:Int,b:Int){a+b}
sum2: (a: Int, b: Int)Unit

scala> def sum3(a:Int,b:Int){print(a+b)}
sum3: (a: Int, b: Int)Unit
// 调用
scala> sum1(1,2)
res1: Int = 3

scala> sum2(1,2)

scala> sum3(1,2)
3
  • main函数
def main(args: Array[String]) {}
//或
def main(args: Array[String]):Unit={}

懒值

  • 当val被声明为lazy时,初始化将被推迟,直到首次对它取值、使用
  • lazy对于开销较大的初始化语句十分有用
  • 开发懒数据结构的基础。
// 不声明为lazy,如果文件不存在会直接报错
scala> val words=scala.io.Source.fromFile("C:")
java.io.FileNotFoundException: C: (拒绝访问。)
  at java.io.FileInputStream.open0(Native Method)
  at java.io.FileInputStream.open(Unknown Source)
  at java.io.FileInputStream.<init>(Unknown Source)
  at scala.io.Source$.fromFile(Source.scala:91)
  at scala.io.Source$.fromFile(Source.scala:76)
  at scala.io.Source$.fromFile(Source.scala:54)
  ... 32 elided
//声明为lazy,有错误也不会报
scala> lazy val words=scala.io.Source.fromFile("C:")
words: scala.io.BufferedSource = <lazy>
//使用时报错
scala> words.mkString
java.io.FileNotFoundException: C: (拒绝访问。)
  at java.io.FileInputStream.open0(Native Method)
  at java.io.FileInputStream.open(Unknown Source)
  at java.io.FileInputStream.<init>(Unknown Source)
  at scala.io.Source$.fromFile(Source.scala:91)
  at scala.io.Source$.fromFile(Source.scala:76)
  at scala.io.Source$.fromFile(Source.scala:54)
  at .words$lzycompute(<console>:11)
  at .words(<console>:11)
  ... 32 elided

异常

  • 异常工作机制与Java或C++一样
  • 直接抛出异常
  • 如果没有找到符合要求的异常处理,则程序退出
throw new Exception("Some Errors")
// java.lang.Exception: Some Errors
//  ... 32 elided
  • 抛出的对象必须是java.lang.Throwable的子类
  • 与java不同,scala没有“受检”异常,全为不受检异常
  • throw表达式的类型是Nothing,在if/else语句中,如果一个分支的类型是Nothing,那么语句的类型是另一个分支的类型。(好像没用)
  • 捕获异常采用模式匹配,具体的异常放在通用的异常前面。
  • try{ }catch{ }finally{ }语句,无论是否异常,finally语句都会执行
 for(i<- -3 to 3) {
   try {
     println(f"1 ÷ $i = ${1 / i}%.2f")
   } catch {
     case _: Exception => println("hah")
   }
 }
//    1 ÷ -3 = 0.00
//    1 ÷ -2 = 0.00
//    1 ÷ -1 = -1.00
//    hah
//    1 ÷ 1 = 1.00
//    1 ÷ 2 = 0.00
//    1 ÷ 3 = 0.00

你可能感兴趣的:(Scala从入门到住院)