美文网首页KotlinkotlinAndroid开发
Kotlin函数式编程笔记

Kotlin函数式编程笔记

作者: dengyin2000 | 来源:发表于2018-03-07 22:14 被阅读131次

    Kotlin语言基础笔记

    Kotlin流程控制语句笔记

    Kotlin操作符重载与中缀表示法笔记

    Kotlin扩展函数和扩展属性笔记

    Kotlin空指针安全(null-safety)笔记

    Kotlin类型系统笔记

    Kotlin面向对象编程笔记

    Kotlin委托(Delegation)笔记

    Kotlin泛型型笔记

    Kotlin函数式编程笔记

    Kotlin与Java互操作笔记

    Kotlin协程笔记

    就像在Java的世界中,一切皆是对象。那么在函数式编程中当然一切皆是函数。函数式编程感觉像返璞归真,现在又火了起来。Kotlin作为一位这几年才出现的语言必然会支持函数式编程。在Kotlin中函数式一等公民,地位和对象一样高,你可以在方法中输入函数,也可以返回函数。相对于Scala的学院派风格,Kotlin则是纯的的工程派:实用性、简洁性上都要比Scala要好。怪不得我当年看Scala中感觉好像门槛有点高,上手比较难。

    1. 高阶函数

    高阶函数是将函数用作参数或者返回值的函数。Kotlin的Collection类型中有大量的这种高阶函数,例如:Iterable的filter函数

    filter
    其中predicate: (T) -> Boolean定义了一个函数变量,变量名是predicate,类型是(T) -> Boolean这样一个函数。这个函数表示输入参数是T类型,输出Boolean类型。那我们来定义这样一个函数,然后调用高阶函数Iterable.filter
    fun isLargeThanFive(x: Int): Boolean = x > 5
    fun main(args: Array<String>) {
         val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
         println(numbers.filter(::isLargeThanFive))  //打印[6, 7, 8]
    }
    

    注意:我们使用::来引用一个函数。

    2. 匿名函数

    匿名函数,其实就是没有函数名的函数。我们也可以使用匿名函数来实现上面的代码:

    fun main(args: Array<String>) {
         val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
         println(numbers.filter(
                   fun (x: Int):Boolean {
                        return x > 5
                   }
              )
         )
         //or 写成一行
         //println(numbers.filter(fun(x: Int) = x > 5))
    }
    

    3. Lambda表达式

    我们也可以使用更简单Lambda表达式来实现:

        val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
        numbers.filter({ it > 5 })
        //or 这样
        numbers.filter { it > 5 }
    

    Lambda表达式是这样定义的:

    • lambda 表达式总是括在花括号中。
    • 其参数(如果有的话)在 -> 之前声明(参数类型可以省略)。
    • 函数体(如果存在的话)在 -> 后面。
    • 如果lambda表达式是该调用的唯一函数,则调用中的圆括号可以省略。
    • 如果函数的最后一个参数是一个函数,并且你传递一个 lambda 表达式作为相应的参数,你可以在圆括号之外指定它。

    对于最后一点,我们可以看看Iterable.elementAtOrElse方法:

    elementAtOrElse
    这个方法是返回第index元素,如果不存在的话返回(Int) -> T的函数值,函数参数是index,返回T类型。我们可以看看如何调用:
        val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
        println(numbers.elementAtOrElse(10, { 100}))
        //或者可以这样。
        println(numbers.elementAtOrElse(10) {100})
    

    我们现在来定义一个两个参数的lambda表达式:

        val max = { x: Int, y: Int -> if (x > y) x else y }
        //下面这句等同于上面这句代码。
        //val max: (Int, Int) -> Int = { x: Int, y: Int -> if (x > y) x else y }
        println(max(1, 2))
    

    更复杂的,lambda表达式还可以返回一个lambda表达式:

        val sum = { x: Int ->  {y:Int -> x+y } }
        println(sum)  //打印(kotlin.Int) -> (kotlin.Int) -> kotlin.Int
    
        println(sum(2)(1))  //打印3
    

    3.1 it单个参数的隐私名称

    当lambda表达式只有一个参数时,那么它的声明可以省略(包括->)。可以用it代表lambda唯一的参数。

        val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
        numbers.filter({ it > 5 })
        //or 这样
        numbers.filter { it > 5 }
    

    3.2 下划线用于未使用的变量(1.1版本起)

    如果 lambda 表达式的参数未使用,那么可以用下划线取代其名称:

    map.forEach { _, value -> println("$value!") }
    

    4. 闭包

    Lambda 表达式或者匿名函数,以及局部函数和对象表达式(object declarations)可以访问其 闭包,即在外部作用域中声明的变量。 与 Java 不同的是可以修改闭包中捕获的变量:

    fun main(args: Array<String>) {
        val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
        var sum = 0
        numbers.filter { it > 5 }.forEach { sum += it }
        println(sum)  //打印21
    }
    

    5. 带接收者的函数字面值

    使用匿名函数的语法,我们可以直接指定函数字面值的接收者类型。

    val sum = fun Int.(other: Int): Int = this + other
    println(1.sum(1))  打印2
    

    当接收者类型可以从上下文推断时,lambda 表达式可以用作带接收者的函数字面值。

    class HTML {
        fun body() {
            println("HTML BODY")
        }
    }
    
    fun html(init: HTML.() -> Unit): HTML { // HTML.()中的HTML是接受者类型
        val html = HTML()  // 创建接收者对象
        html.init()        // 将该接收者对象传给该 lambda
        return html
    }
    
    fun main(args: Array<String>) {
        html{
            body()
        }  //打印HTML BODY
    }
    

    这个特性可以让我们构建DSL语言,类似安卓中build.gradle中的语法。

    相关文章

      网友评论

        本文标题:Kotlin函数式编程笔记

        本文链接:https://www.haomeiwen.com/subject/vcqsfftx.html