Kotlin教程

Android开发之Kotlin从入门到精通总结(三)---函数式编程基石——高阶函数和Lambda表达式

本文主要是介绍Android开发之Kotlin从入门到精通总结(三)---函数式编程基石——高阶函数和Lambda表达式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

(一)、函数式编程简介

1.1 函数式编程(functional programming)

函数式编程是一种编程典范,也就是面向函数的编程。 在函数式编程中一切都是函数。 函数式编程核心概念如下: ① 函数是 “ 一等公民 ” :是指函数与其他数据类型(Int、Double、String等)是一样的,处于平等的地位。函数可以作为其他函数的参数传入,也可以作为其他函数的返回值返回。 ② 使用表达式,不用语句:函数式编程关心的输入和输出,即:参数和返回值。在程序中使用表达式可以有返回值,而语句没有。例如:控制结构中的if 和 when 结构都属于表达式。 ③ 高阶函数:函数式编程支持高阶函数,所谓高阶函数就是一个函数可以作为另外一个函数的参数或返回值。 ④ 无副作用:是指函数执行过程会返回一个结果,不会修改外部变量,这就是 “ 纯函数” ,同样的输入参数一定会有同样的输出结果。 Kotlin 语言支持函数式编程,提供了函数类型、高阶函数和 Lambda 表达式。  

1.2 高阶函数 和  函数类型

  ① 高阶函数的概念:函数式编程的关键是高阶函数的支持。一个函数可以作为另一个函数的参数,或者返回值,那么这个函数就是“高阶函数 ” 。    ② 函数类型概念:Kotlin中每一个函数都有一个类型,称为 “ 函数类型 ” ,函数类型作为一种数据类型与数据类型在使用场景没有区别。可以声明变量,也可以作为其他函数的参数或者其他函数的   返回值使用。函数类型就是把函数参数列表中的参数类型保留下来,再加上箭头符号和返回类型,形式如下:
参数列表中的参数类型 -> 返回类型

举例:

package com.work.section 
//定义计算长方形面积函数 
//函数类型: (Double, Double) -> Double 
fun rectangleArea(width: Double, height: Double): Double {  
    return width * height 
}

//定义计算三角形面积函数 
//函数类型: (Double, Double) -> Double 
fun triangleArea(bottom: Double, height: Double) = 0.5 * bottom * height 

//函数类型: ()->Unit 
fun sayHello() { 
    print("Hello, World") 
}

fun main(args: Array<String>) { 

   val getArea: (Double, Double) -> Double = ::triangleArea  

   //调用函数 
   val area = getArea(50.0, 40.0) 
   print(area) //1000.0 
}

每一个函数都有函数类型,即便是函数列表中没有参数,以及没有返回值的函数也有函数类型,如代码sayHello()函数,sayHello()函数的函数类型是()->Unit。

1.3 函数字面量

函数类型可以声明的变量,那么函数类型变量能够接收什么的数据呢?即函数字面量如何表示的问题,函数字面量可以有三种表示: ① 函数引用。引用到一个已经定义好的,有名字的函数。它可以作为函数字面量。 ② 匿名函数。没有名字的函数,即匿名函数,它也可以作为函数字面量。 ③ Lambda 表达式。 Lambda 表达式是一种匿名函数,可以作为函数字面量。  
package com.work.section 
 //该函数的返回值类型是:函数类型,即(Int, Int) -> Int
 fun calculate(opr: Char): (Int, Int) -> Int { 
     //加法函数 
     fun add(a: Int, b: Int): Int { 
        return a + b 
     }
     //减法函数 
     fun sub(a: Int, b: Int): Int { 
        return a - b 
     }

     val result: (Int, Int) -> Int = 
         when (opr) { 
             //函数引用,采用“双冒号加函数名”形式引用,add和sub是两个局部函数,它们的函数引用表示方式是::add和::sub
             '+' -> ::add 
             '-' -> ::sub  
             //匿名函数
             '*' -> { 
                     //乘法匿名函数 
                      fun(a: Int, b: Int): Int {  
                         return (a * b) 
                      } 
              } else -> { a, b -> (a / b) } //除法Lambda表达式  
           }
           return result 
         } 

fun main(args: Array<String>) { 
     //f0的类型是函数类型,即(Int, Int) -> Int
     val f0:(Int, Int) -> Int = calculate('+') 
     println(f0(10, 5)) //调用f0变量

     //如果表达式返回类型确定了,则可以省略变量后的类型声明
     val f1 = calculate('+')  
     println(f1(10, 5)) //调用f1变量  
     
     //f2的类型是函数类型,即(Int, Int) -> Int  
     val f2 = calculate('-') 
     println(f2(10, 5)) 

     //f3的类型是函数类型,即(Int, Int) -> Int
     val f3 = calculate('*') 
     println(f3(10, 5)) 

     //f4的类型是函数类型,即(Int, Int) -> Int
     val f4 = calculate('/') 
     println(f4(10, 5)) 
}

其中,calculate函数的返回类型就是(Int, Int) -> Int函数类型,说明calculate是高阶函数。

输出结果:

2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: --------函数类型输出 加、加、减、乘、除-------
2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: 15
2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: 15
2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: 5
2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: 50
2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: 2

1.4 函数作为另一个函数返回值使用

可以把函数作为另一个函数的返回值使用,那么这个函数属于高阶函数。

package com.work.section 

//定义计算长方形面积函数 
//函数类型: (Double, Double) -> Double 
fun rectangleArea(width: Double, height: Double): Double {  
    return width * height 
}

//定义计算三角形面积函数 
//函数类型: (Double, Double) -> Double 
fun triangleArea(bottom: Double, height: Double) = 0.5 * bottom * height 

//返回类型为函数类型,即 (Double, Double) -> Double
fun getArea(type: String): (Double, Double) -> Double { 
  var returnFunction: (Double, Double) -> Double;
  
   when (type) { 
       "rect" -> //rect 表示长方形 
              returnFunction = ::rectangleArea 
        else -> //tria 表示三角形 
              returnFunction = ::triangleArea  
   }
   return returnFunction  
}
fun main(args: Array<String>) { 

    //获得计算三角形面积函数 
    var area: (Double, Double) -> Double = getArea("tria")  
    println("底10 高13,计算三角形面积:${area(10.0, 15.0)}")  

    //获得计算长方形面积函数 
    area = getArea("rect") 
    println("宽10 高15,计算长方形面积:${area(10.0, 15.0)}") 
}

 

1.5 函数作为参数使用

作为高阶函数还可以接收另一个函数作为参数使用。
//定义计算长方形面积函数 
//函数类型: (Double, Double) -> Double 
fun rectangleArea(width: Double, height: Double): Double {  
    return width * height 
}

//定义计算三角形面积函数 
//函数类型: (Double, Double) -> Double 
fun triangleArea(bottom: Double, height: Double) = 0.5 * bottom * height 

//高阶函数,funcName参数是函数类型:(Double, Double) -> Double
fun getAreaByFunc(funcName: (Double, Double) -> Double, a: Double, b: Double): Double {  
   return funcName(a, b) 
}

fun main(args: Array<String>) { 
    //获得计算三角形面积函数 
    var result = getAreaByFunc(::triangleArea, 10.0, 15.0) ② 
    println("底10 高15,计算三角形面积:$result") ③ 

    //获得计算长方形面积函数 
    result = getAreaByFunc(::rectangleArea, 10.0, 15.0) ④ 
    println("宽10 高15,计算长方形面积:$result") ⑤ 
}

运行结果:

底10 高15,三角形面积:75.0
宽10 高15,计算长方形面积:150.0

(二)lambda表达式

2.1 Lambda表达式

Lambda 表达式是一种匿名函数,可以作为表达式、函数参数和函数返回值使用,Lambda 表达式的运算结果是一个函数。 Lambda表达式标准语法格式 :Kotlin中的 Lambda 表达式很灵活,其标准语法格式如下:
{ 参数列表 -> 
        Lambda体 
}
其中, Lambda 表达式的参数列表与函数的参数列表形式类似,但是 Lambda 表达式参数列表前后没有小括号。箭头符号将参数列表与Lambda 体分隔开, Lambda 表达式不需要声明返回类型。 Lambda 表达式可以有返回值,如果没有 return 语句 Lambda 体的最后一个表达式就是Lambda 表达式的返回值,如果有 return 语句返回值是 return 语句后面的表达式。   提示:  Lambda 表达式与函数、匿名函数一样都有函数类型,但从 Lambda 表达式的定义中只能看到参数类型,看不到返回类型声明,那是因为返回类型可以通过上下文推导出来。
private fun calculate(opr: Char): (Int, Int) -> Int { 
      return when (opr) { 
          //lambda表达式
          '+' -> { a: Int, b: Int -> a + b } 
          '-' -> { a: Int, b: Int -> a - b }  
          '*' -> { a: Int, b: Int -> a * b }  
          else -> { a: Int, b: Int -> a / b } 
         } 
}

fun main(args: Array<String>) { 
      val f1 = calculate('+')  
      println(f1(10, 5)) //调用f1变量  

      val f2 = calculate('-') 
      println(f2(10, 5)) 

      val f3 = calculate('*') 
      println(f3(10, 5)) 

      val f4 = calculate('/') 
      println(f4(10, 5)) 
}

Lambda表达式也是函数类型,可以声明变量,也可以作为其他函数的参数或者返回值使用。

2.2 lambda表达式的简化用法

①参数类型推导简化 类型推导是 Kotlin 的强项, Kotlin 编译器可以根据上下文环境推导出参数类型和返回值类型。以下代码是标准形式的Lambda 表达式:
{ a: Int, b: Int -> a + b }

Kotlin能推导出参数a和b是Int类型,当然返回值也是Int类型。简化形式如下:

{ a, b -> a + b }

② 使用尾随Lambda表达式  

Lambda表达式可以作为函数的参数传递,如果Lambda表达式很长,就会影响程序的可读性。如果一个函数的最后一个参数是Lambda表达式,那么这个Lambda表达式可以放在函数括号之后(注意这里所说的lambda表达式是包括花括号"{}"的)。示例代码如下:

fun calculatePrint1(funN: (Int, Int) -> Int) { //参数是函数类型 
    //使用funN参数 
    println("${funN(10, 5)}") 
}

//打印计算结果函数 最后一个参数是函数类型:(Int, Int) -> Int 
fun calculatePrint(n1: Int, n2: Int, opr: Char, funN: (Int, Int) -> Int) {
     println("${n1} ${opr} ${n2} = ${funN(n1, n2)}") 
}

fun main(args: Array<String>) { 
     calculatePrint(10, 5, '+', { a, b -> a + b })//标准形式 
     calculatePrint(10, 5, '-') { a, b -> a - b }//尾随Lambda表达式形式  

     //最后一个参数是lambda表达式:{ a, b -> a + b }
     calculatePrint1({ a, b -> a + b })//标准形式 
     calculatePrint1() { a, b -> a + b }//尾随Lambda表达式形式  
     calculatePrint1 { a, b -> a + b }//尾随Lambda表达式,如果小括号里没有参数可省略小括号 
}

注意:由于calculatePrint1函数只有一个lambda表达式作为参数,它采用了尾随Lambda表达式形式,这样一来它的小括号中就没有参数了,这种情况下可以省略小括号。

③ 省略参数声明

如果Lambda表达式的参数只有一个,并且能够根据上下文环境推导出它的数据类型,那么这个参数声明可以省略,在Lambda体中使用隐式参数it替代Lambda表达式的参数。

fun revreseAndPrint(str: String, funN: (String) -> String) { 
      val result = funN(str) 
      println(result) 
}
fun main(args: Array<String>) { 
      revreseAndPrint("hello", { s -> s.reversed() })//标准形式 
      revreseAndPrint("hello", { it.reversed() })//省略参数,使用隐式参数it  
      
      //由于result1被未指定数据类型,编译器不能推导出来Lambda表达式的参数类型,所以不能使用it。
      val result1 = { a: Int -> println(a) }//不能省略参数声明  
      
      //由于result2被指定了数据类型(Int)->Unit,编译器能推导出 Lambda表达式的参数类型,所以可以使用it。
      val result2:(Int)->Unit = { println(it) }//可以省略参数声明  
      result2(30) //输出结果是30 
}

注意 Lambda体中it隐式变量是由Kotlin编译器生成的,它的使用有两个前提:一是Lambda表达式只有一个参数,二是根据上下文能够推导出参数类型。

lambda表达式与return语句

Lambda 表达式体中也可以使用 return 语句,它会使程序跳出 Lambda表达式体。 提示 forEach 是集合、数组或区间的函数,如果它后面是一个 Lambda 表达式,集合、数组或区间对象调用forEach 函数时,会将它们的每一个元素传递给 Lambda 表达式并执行。 示例代码如下:
//累加求和函数 
fun sum(vararg num: Int): Int { 
    var total = 0 
    //forEach后面跟的是一个lambda表达式
    num.forEach { 
          //if (it == 10) return -1 //返回到最近的函数,即sum函数  
          //@forEach是隐式声明标签,标签名是Lambda表达式所在函数名(forEach)。
          if (it == 10) return@forEach//返回Lambda表达式函数  
          total += it 
        }
    return total 
}

fun main(args: Array<String>) { 
      val n = sum(1, 2, 10, 3) 
      println(n) //6 

      //label@是Lambda表达式显示声明标签
      val add = label@ {  
                    val a = 1 
                    val b = 2 
                    return@label 6666  
                    a + b 
                 }
      //调用Lambda表达式add 
       println(add()) //6666 
}

上述代码num.forEach是使用了forEach函数,它后面的Lambda表达式,如果使用代码 if (it == 10) return -1语句,则会返回最近的函数,即sum函数,不是返回 Lambda表达式forEach。为了返回Lambda表达式则需要在return语句后面加上标签,即“if (it == 10) return@forEach”,@forEach是隐式声明标签,标签名是Lambda表达式所在函数名(forEach)。也可以为Lambda表达式声明显示标签,代码label@是Lambda表达式显示声明标签,代码“ return@label 10 ”是使用显示标签。

2021-04-19 18:04:19.279 28687-28687/com.xw.kotlinforandroid I/System.out: 6
2021-04-19 18:04:19.279 28687-28687/com.xw.kotlinforandroid I/System.out: 6666

 

这篇关于Android开发之Kotlin从入门到精通总结(三)---函数式编程基石——高阶函数和Lambda表达式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!