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) //
}
-
程序执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
-
函数的局部变量是独立的,不会相互影响。引用类型除外
-
递归必须向退出递归的条件逼近,否则就是无限递归
-
当一个函数执行完毕,或者遇到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
}
函数注意事项
- 函数的形参列表可以是多个, 如果函数没有形参,定义和调用时,都可以不带()
2)如果省略= 只做执行代码块用,打印时会返回(),因为一切表达式都有返回值。
-
形参列表和返回值列表的数据类型,可以是值类型和引用类型。
-
scala 函数的形参默认是val的(局部变量),因此不能在函数中进行修改。
-
如果函数明确使用return关键字,那么函数类型就不能自行推断了,多种返回类型的可自动或手动指定any
-
如果函数明确声明无返回值(声明Unit),那么函数体中即使使用return关键字也不会有返回值
-
Scala语法中任何的语法结构都可以嵌套其他语法结构(灵活),即:函数/方法中可以再声明/定义函数/方法,类中可以再声明类,方法中可以再声明/定义方法(多个位置不同的同名函数如何使用?)
-
Scala中的函数可以根据函数体最后一行代码,自行推断函数返回值类型。所以return关键字可以省略。
-
递归函数未执行之前是无法推断出来结果类型,在使用时必须有明确的返回值类型
过程
将函数的返回类型为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子句来匹配异常
-
我们将可疑代码封装在try块中。 在try块之后使用了一个catch处理程序来捕获异常。如果发生任何异常,catch处理程序将处理它,程序将不会异常终止。
-
可以有多个catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大
-
常类写在后面,否则编译错误。会提示 “Exception ‘java.lang.xxxxxx’ has already been caught”
-
Scala的异常的工作机制和Java一样,但是Scala没有“checked(编译期)”异常,即Scala没有编译异常这个概念,异常都是在运行的时候捕获处理。
-
用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型。throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方
def test(): Nothing = {
throw new Exception("不对")
}
- finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和Java一样。
- 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)
}