天天看点

疯狂Kotlin讲义学习笔记06章:函数、lambda表达式

1、定义和调用函数及函数返回值Unit

fun 函数名 (形参列表) : 返回值类型{

函数体

}

可以省略返回值类型

如果返回值类型为:Unit则为无返回值,等同于java的void

2、递归函数

fun main() {
   var n=5
   var resualt= jiechen(n)
    println(resualt)
}

fun jiechen(n:Int):Int{
    if (n==1){
        return 1
    }else{
        return n* jiechen(n-1)
    }
}
           

打印

120

3、单表达式函数

函数体只有一行语句的时候,省略掉大括号的写法

fun main() {
   var a=5.25
   var b=3.4
    println(area(a,b))
}

fun area(x:Double,y:Double):Double=x*y;
           

打印:

17.849999999999998

4、形参默认值

形参名:形参类型=默认值

fun main() {
   var a=5.25
   var b=3.4
    println(area(a,b))
}

fun area(x:Double,y:Double,pi:Double=3.1415):Double=x*y*pi;
           

打印:

56.07577499999999

fun main() {
   var a=5.25
   var b=3.4
    println(area(a,b,5.5))//这里覆盖了pi的默认值
}

fun area(x:Double,y:Double,pi:Double=3.1415):Double=x*y*pi;
           

打印:

98.17499999999998

fun main() {
   var a=5.25
   var b=3.4
    println(area(a,5.5))//按顺序5.5赋值给了y
}

fun area(x:Double,y:Double,pi:Double=3.1415):Double=x*y*pi;
           

打印

90.7108125

5、尾递归函数

当函数将调用自身作为它执行的最后一行大麦,且递归调用后没有更多代码时,可以使用尾递归语法,当然需要使用tailrec修饰符

以下函数形式:

fun main() {
   var n=5
   var resualt= jiechen(n)
    println(resualt)
}

fun jiechen(n:Int):Int{
    if (n==1){
        return 1
    }else{
        return n* jiechen(n-1)
    }
}
           

都可以改成为

fun main() {
    var n=5
    var resualt= jiechen(n)
    println(resualt)
}

tailrec  fun jiechen(n:Int,total:Int=1):Int=if (n==1) total else jiechen(n-1,total*n)
           

以上两段代码都打印120

6、个数可变的形参

需要加vararg修饰符,实际上就是把可变形参当作一个数组来处理

fun main() {
    var n=5

    iterator(n,"java","swift","android")
}

fun iterator(a:Int,vararg books:String){
    for (b in books){
        println(b+a)
    }
}
           

打印

7、函数重载

同名函数,形参列表或返回值不同即可以重载

fun main() {
    var n=5

    overridefun()
    overridefun("zhangsan")
    overridefun("zhangsan",10)
}

fun overridefun(){
    println("无参数的重载")
}

fun overridefun(str:String){
    println("带一个参数${str}的重载")
}
fun overridefun(name:String, age:Int){
    println("带两个参数的重载,名字:${name},年龄:${age}")
}
           

打印:

无参数的重载

带一个参数zhangsan的重载

带两个参数的重载,名字:zhangsan,年龄:10

8、局部函数

就是函数内部的函数

fun main() {
    println(innerFun(2,5))
}

fun innerFun(type: Int, width: Int):Int {
    var resualt=0
    //计算平方
    fun squre(width: Int): Int {
        return width * width
    }

    //计算立方
    fun cube(width: Int): Int {
        return width * width * width
    }

    when (type) {
        1 -> return  squre(width)
        2 -> return cube(width)
        else-> return 0
    }
}
           

打印

125

9、高阶函数–函数类型

函数类型指函数的参数类型,->符号,返回类型,这三者形成,通过对函数应用,可以让我们的函数使用更加灵活

fun main() {
    //定义一个函数类型为:(Int,Int)->Int
    var myfun:(Int,Int)->Int
    myfun=::caculateFun

    println(myfun(1,5))
}

fun caculateFun(type: Int, width: Int):Int {
    var resualt=0
    //计算平方
    fun squre(width: Int): Int {
        return width * width
    }

    //计算立方
    fun cube(width: Int): Int {
        return width * width * width
    }

    when (type) {
        1 -> return  squre(width)
        2 -> return cube(width)
        else-> return 0
    }
}
           

打印:

25

10、使用函数类型作为形参类型

当函数的大部分逻辑都能确定,但某些处理逻辑暂时无法确定,意味着某些程序代码需要动态改变,这时候可以在形参中定义函数类型的参数,

fun main() {
    //定义一个整形数组
    var data= arrayOf(3,4,5,7,9,45)
    //原数据
    println("data的原数据为:"+data.contentToString())
    //平方计算
    println("每个数组元素平方"+map(data, ::squre).contentToString())//这里只需要传入参数名即可,不需要给定参数,因为在应用的函数里面已经给定参数了
    //立方计算
    println("每个数组元素立方"+map(data, ::cube).contentToString())//这里只需要传入参数名即可,不需要给定参数,因为在应用的函数里面已经给定参数了

}

fun map(data:Array<Int>,fn:(Int)->Int):Array<Int>{
    var resualt=Array<Int>(data.size,{0})
    for (i in data.indices){
        resualt[i]=fn(data[i])
    }
    return  resualt

}
//计算平方

fun squre(width: Int): Int {
    return width * width
}

//计算立方
fun cube(width: Int): Int {
    return width * width * width
}
           

打印:

data的原数据为:[3, 4, 5, 7, 9, 45]

每个数组元素平方[9, 16, 25, 49, 81, 2025]

每个数组元素立方[27, 64, 125, 343, 729, 91125]

11、使用函数类型作为返回值类型

顾名思义

fun main() {
    var mathFun= getMathFun(1)
    println(mathFun(5))
    mathFun= getMathFun(2)
    println(mathFun(5))

}

fun getMathFun(type: Int):(Int)->Int {
    var resualt=0
    //计算平方
    fun squre(width: Int): Int {
        return width * width
    }

    //计算立方
    fun cube(width: Int): Int {
        return width * width * width
    }

    when (type) {
        1 -> return  ::squre
        else -> return ::cube
    }
}
           

25

125

12、局部函数与Lambda表达式

lambda和函数的区别

  • lambda表达式总是被大括号包裹
  • 定义lambda表达式不需要fun关键字,无须指定函数名
  • 形参列表(如果有的话)在->之前申明,参数类型可以省略
  • 函数体(lambda表达式执行体)放在->之后
  • 函数的最后一个表达式自动被作为lambda表达式的返回值,无需使用return关键字
fun main() {
    var mathFun= getMathFun(1)
    println(mathFun(5))
    mathFun= getMathFun(2)
    println(mathFun(5))

}

fun getMathFun(type: Int):(Int)->Int {
    when (type) {
        1 -> return { width:Int->Int
            width*width
        }
        else -> return { width:Int->Int
            width*width*width
        }
    }
}
           

打印

25

125

13、Lambda表达式的脱离

作为函数参数传入的lambad表达式可以脱离函数独立使用。

import kotlin.collections.ArrayList
//定义一个List类型变量,泛型类型为函数类型
var lambdaList=ArrayList<(Int)->Int>()
//定义一个函数,该函数的形参为函数
fun collectFun(fn:(Int)->Int){
    lambdaList.add(fn)
}
fun main() {
    //调用函数两次,将会向lambdaList中添加元素,每个元素都是lambda表达式
     collectFun ({it*it})
    collectFun ({it*it*it})
    println(lambdaList.size)
    //依次调用lambdaList集合的元素(每个元素都是lambda表达式)
    for (i in lambdaList.indices){
        //调用每一个元素(函数),参数在序号的基础上+10
        println(lambdaList[i](i+10))
    }
}
           

打印:

2

100

1331

14、Lambda表达式

lambda表达式标准语法

{

(形参列表)->

//零到多条可执行语句

}

15、调用Lambda表达式

直接将lambda表达式赋值给变量或者直接调用lambda表达式

fun main() {
    var square={n:Int->
        n*n
    }

    println(square(5))

    var result={base:Int,exponent:Int->
        var result=1
        for (i in 1..exponent){
            result*=base
        }
        result//lambda的最后一条语句为返回语句
    }(4,5)//这里直接定义玩就立刻使用

    println(result)
}
           

打印:

25

1024

16、利用上下文推断类型

如果上下文可以推断出形参的类型,则可以省略lambda表达式的形参类型

fun main() {
    //由于返回值类型可以确定,所以可以推断出形参类型
    var sqare:(Int)->Int={n-> n*n}

    print(sqare(5))
}
           

打印

25

17、省略形参名

如果只有一个形参,则可以省略形参名,同时->符号也可以一通省略,lambda表达式使用 it 来代表形参

fun main() {
    //由于返回值类型可以确定,所以可以推断出形参类型
    var sqare:(Int)->Int={it*it}

    print(sqare(6))
}
           

打印

36

18、调用Lambda表达式的约定(尾随闭包)

如果函数的最后一个参数是函数类型,而且你打算传入一个lambda表达式作为相应的参数,那么就允许在圆括号之外指定lambda表达式

fun main() {
    var list= listOf<String>("java","kotlin","go")
    //最后一个参数是lambda表达式,可将表达式写在圆括号外面
    var rt=list.dropWhile (){ it.length>3 }
    println(rt)

    var map= mutableMapOf("疯狂安卓讲义" to 56)
    list.associateTo (map){"疯狂${it}讲义" to it.length  }
    println(map)

}
           

[go]

{疯狂安卓讲义=56, 疯狂java讲义=4, 疯狂kotlin讲义=6, 疯狂go讲义=2}

19、个数可变的参数和lambda参数

当被调用的函数既包含个数可变的形参,也包含函数类型的形参,那么就应该将函数类型的的形参放在最后,此外lambda也无须使用命名参数

fun <T>test(vararg names:String,transform:(String)->T):List<T>{
    var mutableList:MutableList<T> = mutableListOf()
    for (name in names){
        mutableList.add(transform(name))//使用了参数类型
    }

    return mutableList.toList()
}


fun main() {
   //将lambda表达式放在圆括号后面,无需使用命名参数
    var list1= test("java","kotlin","go"){it.length}
    println(list1)

    var list2= test("java","kotlin","go"){"疯狂${it}讲义"}
    println(list2)

}
           

打印

[4, 6, 2]

[疯狂java讲义, 疯狂kotlin讲义, 疯狂go讲义]

20、匿名函数的用法

同java的匿名函数

如果匿名函数的形参类型可以推断出来,匿名函数的形参类型也可以不写

定义的匿名函数的函数体如果是单表达式,可以省略声明函数的返回值

fun main() {
    var test=fun(x:Int,y:Int):Int{
        return x+y
    }

    println(test(3,4))

    //省略形参类型
    var filteredList= listOf(3,4,5,6,7,9,34,56,32).filter (
        //使用匿名函数作为filter()方法的参数
        fun (el):Boolean{
            return Math.abs(el)>20
        })

    println(filteredList)

    //定义的函数如果是单表达式,省略形参类型及返回值类型
    var wawa=fun(x:Int,y:Int)=x+y
    println(wawa(3,5))

    var rt=listOf(3,4,5,6,7,9,34,56,32).filter (
        //使用匿名函数作为filter()方法的参数
        fun (el)=Math.abs(el)>20
    )

    println(rt)
}
           

打印:

7

[34, 56, 32]

8

[34, 56, 32]

21、匿名函数和Lambda表达式的return

匿名函数的return用于返回函数本身,而lambda表达式的return用于返回它所在的函数,而不是返回lambda表达式

fun main() {
    var list= listOf<Int>(3,4,5,678,23)
    //使用匿名函数返回函数本身
    list.forEach(fun(n){
        println("元素依次为:${n}")
        return //返回匿名函数本身
    })
    println("------")
    //使用匿名的lambda表达式返回lambda表达式所在的函数,所以无法正确依次输出,输出第一个以后就return了
    list.forEach({ n ->
        println("元素依次为:${n}")
        return//返回所在的函数,main函数
    })
}
           

打印

元素依次为:3
元素依次为:4
元素依次为:5
元素依次为:678
元素依次为:23
------
元素依次为:3
           

22、捕获上下文中的变量和常量

lambda表达式或是匿名函数(以及局部函数、对象表达式)可以访问或修改器所在上下文(俗称闭包)中的变量和常量,这个过程称之为捕获。即使定义这些变量和常量的作用域已经不存在了,lambda表达式或匿名函数也依然可以访问或修改他们。

//定义一个函数,该函数的返回值类型()->List<String>
fun makeList(ele:String):()->List<String>{
    //创建一个空的list集合
    var list:MutableList<String> = mutableListOf()
    fun addElement():List<String>{
        //向list集合中添加一个元素
        list.add(ele)//这里的ele是从外层函数参数中捕获的
        return list
    }
    return ::addElement//返回一个函数
}
           

返回的事是list的副本,而非list本身

23、内联函数概述

高阶函数调用过程中式有调用函数去复制+粘贴被调用函数的。这种工作通常通过内联函数来实现

在复制+粘贴的代码量不是太高的情况下,可以使用,如果复制粘贴量比较大,则不建议使用内联函数

24、内联函数的使用

内联函数使用inline关键字修饰

fun main() {
    var arr= arrayOf(29,35,67,8,9)
    var mappedResult= map(arr,{it+3})
    println(mappedResult.contentToString())
}

inline fun map(data:Array<Int>,fn:(Int)->Int):Array<Int>{
    var result=Array<Int>(data.size,{0})
    for (i in data.indices){
        result[i]=fn(data[i])
    }
    return  result
}
           

打印

[32, 38, 70, 11, 12]

25、部分禁止内联

使用noinline用于显示阻止某一个或某几个形参内联化

fun main() {
    test({it*it},{"《疯狂讲${it}义》"})
}

inline fun test(fn1:(Int)->Int,noinline fn2:(String)->String){
    println(fn1(20))
    println(fn2("kotlin"))
}
           

打印

400

《疯狂讲kotlin义》

继续阅读