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 be12.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 【2 控制结构和函数】首先导入 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 = //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)Stringadd2(3) // String = 3,10,100add2(3,20) // String = 3,20,100add2(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)Intscala> def sum2(a:Int,b:Int){a+b} sum2: (a: Int, b: Int)Unitscala> def sum3(a:Int,b:Int){print(a+b)} sum3: (a: Int, b: Int)Unit // 调用 scala> sum1(1,2) res1: Int = 3scala> 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.(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 = //使用时报错 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.(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(:11) at .words(: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

    推荐阅读