Scala的基本语法(三)、函数与方法

三、函数与方法

Scala 中 使用val 语句可以定义函数,任何以def 语句定义的都是方法(见《Scala编程》)。Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。

函数与方法

默认参数和带名参数

​ Scala函数的形参,在声明参数时,可直接赋初始值(默认值),如果没有指定实参,则会使用默认值。如果指定了实参,则实参会覆盖默认值

​ 如果存在多个参数,每一个参数都可以设定默认值,那么这个时候,传递的参数到底是覆盖默认值,还是赋值给没有默认值的参数,就不确定了(默认按照声明顺序[从左到右])。在这种情况下,可以采用带名参数

可变参数

如(args:Int*),参数为Seq类型的参数,正常情况下参数必须是单个整数,如果传入区间,需要使用:__*

如果还有其他固定参数,可变参数必须放在最后

递归与迭代

一个函数/方法在函数/方法体内又调用了本身,我们称为递归调用,

def test (n: Int) {
    if (n > 2) {
      test (n - 1)
    }//此处加else 打印2 不加else打印 2 3 4 (栈内存中都会开辟4个空间)
    println("n=" + n) // 
  }
  1. 程序执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)

  2. 函数的局部变量是独立的,不会相互影响。引用类型除外

  3. 递归必须向退出递归的条件逼近,否则就是无限递归

  4. 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁。

5)必须有返回值类型,不然编译器无法校验类型(自动转换是函数结束的时候才能推断)

  • 编写递归函数的关键是找到开始参数和结束条件

猴子吃桃子问题有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,想再吃时(还没吃),发现只有1个桃子了。问题:最初共多少个桃子?

①递归(倒序)

 def peach(day:Int):Int={     //输入天数返回当天的桃子数量
    if(day==10) 1 else (peach(day+1)+1)*2
  }

递归时有多个参数且有默认值

def peach(day:Int,n:Int=1):Int={   //加入有默认值的参数 类型为val
    if(day==10) n else (peach(day+1,n)+1)*2 //迭代时注意参数要一致,此处有默认参数,不写不会报错但结果不对
  }

②迭代

  def eat(day:Int)={
    var n=1;//后面的n是可变变量 所以n不能直接写在形参里
    for(i<- 1 until day)  n=(n+1)*2  //不包含第10天,所以用until
    n
  }

函数注意事项

  1. 函数的形参列表可以是多个, 如果函数没有形参,定义和调用时,都可以不带()

2)如果省略= 只做执行代码块用,打印时会返回(),因为一切表达式都有返回值。

  1. 形参列表和返回值列表的数据类型,可以是值类型和引用类型。

  2. scala 函数的形参默认是val的(局部变量),因此不能在函数中进行修改。

  3. 如果函数明确使用return关键字,那么函数类型就不能自行推断了,多种返回类型的可自动或手动指定any

  4. 如果函数明确声明无返回值(声明Unit),那么函数体中即使使用return关键字也不会有返回值

  5. Scala语法中任何的语法结构都可以嵌套其他语法结构(灵活),即:函数/方法中可以再声明/定义函数/方法,类中可以再声明类,方法中可以再声明/定义方法(多个位置不同的同名函数如何使用?)

  6. Scala中的函数可以根据函数体最后一行代码,自行推断函数返回值类型。所以return关键字可以省略。

  7. 递归函数未执行之前是无法推断出来结果类型,在使用时必须有明确的返回值类型

过程

​ 将函数的返回类型为Unit的函数称之为过程(procedure),如果明确函数没有返回值,那么等号可以省略

惰性函数

​ 当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数称之为惰性函数,在Java的某些框架代码中称之为懒加载(延迟加载)。

​ 1) lazy 不能修饰 var 类型的变量

​ 2) 不但是 在调用函数时,加了 lazy ,会导致函数的执行被推迟,我们在声明一个变量时,如果给声明了 lazy ,那么变量值得分配也会推迟。 比如 lazy val i = 10

异常

​ Scala提供try和catch块来处理异常。try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。可以根据需要在程序中有任意数量的try…catch块。

​ 按照try—catch- case ex: ArithmeticException =>{} …—finally的方式来处理异常

Tip

​ 在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case子句来匹配异常

  1. 我们将可疑代码封装在try块中。 在try块之后使用了一个catch处理程序来捕获异常。如果发生任何异常,catch处理程序将处理它,程序将不会异常终止。

  2. 可以有多个catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大

  3. 常类写在后面,否则编译错误。会提示 “Exception ‘java.lang.xxxxxx’ has already been caught”

  4. Scala的异常的工作机制和Java一样,但是Scala没有“checked(编译期)”异常,即Scala没有编译异常这个概念,异常都是在运行的时候捕获处理。

  5. 用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型。throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方

  def test(): Nothing = {
   throw new Exception("不对")
  }
  1. finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和Java一样。
  2. Scala提供了throws关键字来声明异常。可以使用方法定义声明异常。 它向调用者函数提供了此方法可能引发此异常的信息。 它有助于调用函数处理并将该代码包含在try-catch块中,以避免程序异常终止。在scala中,可以使用throws注释来声明异常

异常处理

中断:breakable(高阶函数) import util.control.Breaks._

breakable {
      while (n <= 20) {
        n += 1
        if (n == 18) {
          //中断
          //def break(): Nothing = { throw breakException }
          break()
        }
        println("n=" + n)
      }
posted @ 2019-12-01 12:12  曲水修竹  阅读(103)  评论(0编辑  收藏  举报