您现在的位置是:首页 >学无止境 >kotlin教程4:函数进阶网站首页学无止境
kotlin教程4:函数进阶
可变参数
kotlin
的函数定义非常灵活,既可以按照顺序传参,也可以通过参数名传参,而且可以设置参数默认值,这些在基础教程中已经讲过了。
此外,kotlin
中用vararg
修饰的参数,为长度可变的参数列表
fun printLst(vararg ts: Int){
for(t in ts){
println(t)
}
}
printLst(1,2,3)
/*
1
2
3
*/
尾递归函数
递归是一种常用的编程技巧,就像之前写的递归式阶乘
fun fac(n:Int):Int{return if(n>1) n*fac(n-1) else 1}
但递归有一个很重要的缺点,即每次调用都要在栈中保存上一个函数的返回地址,如果递归层数太多,会导致栈溢出,所以很多时候会将递归算法改写成动态规划。
为了解决递归问题,kotlin
提供了尾递归方案,即通过tailrec
修饰的函数,如果在函数的最后一行调用自身,那么编译器将自动将递归函数转成迭代形式,而无需担心堆栈错误。
为了理解什么是尾递归,下面以斐波那契数列举个例子
fun fib(n:Int):Int{
if(n<3)
return 1
else
return fib(n-1)+fib(n-2)
}
fun main(){
println(fib(215))
}
这个函数尽管在最后一行调用了自身,但调用自身后还有一个相加的操作,所以是不符合尾递归的要求的。
对此可将其改写为
fun fib(n:Long, a: Long, b: Long): Long {
if (n < 1) return a
else return fib(n-1, b, a + b)
}
这就是尾递归的形式。对比这两种不同的递归方案可以发现,第一种运行之后会卡住,在短时间内没法得出结果,后者则轻而易举地得出了。
>kotlin test.jar
319982248
当然,这个过程并没有使用tailrec
关键字,所以当n
的值比较大的时候一样会出问题
fun main(){
println(fib(12000, 1, 1))
}
会报这个错误
Exception in thread "main" java.lang.StackOverflowError
at TestKt.fib(test.kt:3)
下面可测试一下编译优化的效果
tailrec fun fib(n:Long, a: Long, b: Long): Long {
if (n < 1) return a
else return fib(n-1, b, a + b)
}
fun main(){
println(fib(12000, 1, 1))
}
编译运行,顺利得出结果
>kotlin test.jar
3221758877563775297
函数式
所谓函数式,从敲代码的角度出发,就是把函数作为一种数据类型,可以自由地作为其他函数的参数与返回值。
由于Kotlin
中,函数中所有的参数和返回值都要声明数据类型,所以,函数在作为参数传递时,也要标明函数的输入输出返回值,例如下面代码是函数作为参数的一个例子
fun testFunc(func:(Int)->Int) : (Double)->Double{
return {a:Double -> func(a.toInt()).toDouble()}
}
fun test1(func:(Int)->Int):Int{
return func(5)
}
其中,func:(Int)->Int
为输入参数,func
为参数名,(Int)->Int
为func
作为一种函数的输入输出声明,表示func
是一个输入Int
输出Int
的函数。
(Double)->Double
为testFunc
的返回值类型,表示一个输入Double
输出Double
函数。
换言之testFunc
的功能是,将一个输入输出均为整型的函数,转化为输入输出均为浮点型的函数。
return
的{ -> }
是一个匿名函数,这个在最开始介绍函数的时候就已经讲过了。
接下来测试一下,先新建一个输入整型输出整型的函数
fun square(a:Int):Int{return a*a}
square(3) // 返回值 kotlin.Int = 9
square(3.0) //报错
然后转换,需要注意,当函数作为参数传递时,需要用::
标识
testFunc(::square)(3.0) //返回值9.0
也可以新建一个函数
val dSquare = testFunc(::square)
dSquare(3.0) //kotlin.Double = 9.0